Merge pull request #13117 from overleaf/ii-history-react-changes-list-tests

[web] Add changes list tests

GitOrigin-RevId: 5a4e1ea647d737dc761e4258002e6507c76e95a9
This commit is contained in:
ilkin-overleaf 2023-05-23 10:54:25 +03:00 committed by Copybot
parent 2760ad35fc
commit 454f8f80c2
8 changed files with 295 additions and 41 deletions

View file

@ -14,20 +14,34 @@ function Changes({ pathnames, projectOps }: ChangesProps) {
<ol className="history-version-changes">
{pathnames.map(pathname => (
<li key={pathname}>
<div className="history-version-change-action">
<div
className="history-version-change-action"
data-testid="history-version-change-action"
>
{t('file_action_edited')}
</div>
<div className="history-version-change-doc">{pathname}</div>
<div
className="history-version-change-doc"
data-testid="history-version-change-doc"
>
{pathname}
</div>
</li>
))}
{projectOps.map((op, index) => (
<li key={index}>
<div className="history-version-change-action">
<div
className="history-version-change-action"
data-testid="history-version-change-action"
>
{op.rename && t('file_action_renamed')}
{op.add && t('file_action_created')}
{op.remove && t('file_action_deleted')}
</div>
<div className="history-version-change-doc">
<div
className="history-version-change-doc"
data-testid="history-version-change-doc"
>
{getProjectOpDoc(op)}
</div>
</li>

View file

@ -1,7 +1,6 @@
import { useTranslation } from 'react-i18next'
import { MenuItem, MenuItemProps } from 'react-bootstrap'
import { MenuItem } from 'react-bootstrap'
import Icon from '../../../../../../shared/components/icon'
import * as location from '../../../../../../shared/components/location'
type DownloadProps = {
projectId: string
@ -17,19 +16,12 @@ function Download({
}: DownloadProps) {
const { t } = useTranslation()
const handleDownloadVersion = (e: React.MouseEvent<MenuItemProps>) => {
e.preventDefault()
closeDropdown()
const event = e as typeof e & { target: HTMLAnchorElement }
location.assign(event.target.href)
}
return (
<MenuItem
href={`/project/${projectId}/version/${version}/zip`}
target="_blank"
download={`${projectId}_v${version}.zip`}
rel="noreferrer"
onClick={handleDownloadVersion}
onClick={closeDropdown}
{...props}
>
<Icon type="cloud-download" fw /> {t('history_download_this_version')}

View file

@ -38,6 +38,7 @@ function HistoryVersionDetails({
'history-version-selectable': selectable,
})}
data-testid="history-version-details"
data-selected={selected}
onClick={selectable ? handleSelect : undefined}
>
{children}

View file

@ -66,7 +66,10 @@ function HistoryVersion({
selectable={selectable}
>
<div className="history-version-main-details">
<time className="history-version-metadata-time">
<time
className="history-version-metadata-time"
data-testid="history-version-metadata-time"
>
<b>{formatTime(update.meta.end_ts, 'Do MMMM, h:mm a')}</b>
</time>
{orderedLabels.map(label => (

View file

@ -76,7 +76,10 @@ function LabelListItem({
currentUserId={currentUserId}
label={label}
/>
<time className="history-version-metadata-time">
<time
className="history-version-metadata-time"
data-testid="history-version-metadata-time"
>
{formatTime(label.created_at, 'Do MMMM, h:mm a')}
</time>
{!isPseudoLabel(label) && (

View file

@ -15,7 +15,10 @@ function MetadataUsersList({
const { t } = useTranslation()
return (
<ol className="history-version-metadata-users">
<ol
className="history-version-metadata-users"
data-testid="history-version-metadata-users"
>
{users.map((user, index) => (
<li key={index}>
<UserNameWithColoredBadge user={user} currentUserId={currentUserId} />

View file

@ -9,6 +9,10 @@ import {
import { HistoryProvider } from '../../../../../frontend/js/features/history/context/history-context'
import { updates } from '../fixtures/updates'
import { labels } from '../fixtures/labels'
import {
formatTime,
relativeDate,
} from '../../../../../frontend/js/features/utils/format-date'
const mountWithEditorProviders = (
component: React.ReactNode,
@ -27,6 +31,28 @@ const mountWithEditorProviders = (
}
describe('change list', function () {
const scope = {
ui: { view: 'history', pdfLayout: 'sideBySide', chatOpen: true },
}
const waitForData = () => {
cy.wait('@updates')
cy.wait('@labels')
cy.wait('@diff')
}
beforeEach(function () {
cy.intercept('GET', '/project/*/updates*', {
body: updates,
}).as('updates')
cy.intercept('GET', '/project/*/labels', {
body: labels,
}).as('labels')
cy.intercept('GET', '/project/*/filetree/diff*', {
body: { diff: [{ pathname: 'main.tex' }, { pathname: 'name.tex' }] },
}).as('diff')
})
describe('toggle switch', function () {
it('renders switch buttons', function () {
mountWithEditorProviders(
@ -61,28 +87,6 @@ describe('change list', function () {
})
describe('tags', function () {
const scope = {
ui: { view: 'history', pdfLayout: 'sideBySide', chatOpen: true },
}
const waitForData = () => {
cy.wait('@updates')
cy.wait('@labels')
cy.wait('@diff')
}
beforeEach(function () {
cy.intercept('GET', '/project/*/updates*', {
body: updates,
}).as('updates')
cy.intercept('GET', '/project/*/labels', {
body: labels,
}).as('labels')
cy.intercept('GET', '/project/*/filetree/diff*', {
body: { diff: [{ pathname: 'main.tex' }, { pathname: 'name.tex' }] },
}).as('diff')
})
it('renders tags', function () {
mountWithEditorProviders(<ChangeList />, scope, {
user: {
@ -215,6 +219,234 @@ describe('change list', function () {
cy.wait('@delete')
cy.findByText(labelToDelete).should('not.exist')
})
it('verifies that selecting the same list item will not trigger a new diff', function () {
mountWithEditorProviders(<ChangeList />, scope, {
user: {
id: USER_ID,
email: USER_EMAIL,
isAdmin: true,
},
})
waitForData()
const stub = cy.stub().as('diffStub')
cy.intercept('GET', '/project/*/filetree/diff*', stub).as('diff')
cy.findAllByTestId('history-version-details').eq(1).as('details')
cy.get('@details').click() // 1st click
cy.wait('@diff')
cy.get('@details').click() // 2nd click
cy.get('@diffStub').should('have.been.calledOnce')
})
})
describe('all history', function () {
beforeEach(function () {
mountWithEditorProviders(<ChangeList />, scope, {
user: {
id: USER_ID,
email: USER_EMAIL,
isAdmin: true,
},
})
waitForData()
})
it('shows grouped versions date', function () {
cy.findByText(relativeDate(updates.updates[0].meta.end_ts))
cy.findByText(relativeDate(updates.updates[1].meta.end_ts))
})
it('shows the date of the version', function () {
cy.findAllByTestId('history-version-details')
.eq(0)
.within(() => {
cy.findByTestId('history-version-metadata-time').should(
'have.text',
formatTime(updates.updates[0].meta.end_ts, 'Do MMMM, h:mm a')
)
})
})
it('shows change action', function () {
cy.findAllByTestId('history-version-details')
.eq(0)
.within(() => {
cy.findByTestId('history-version-change-action').should(
'have.text',
'Created'
)
})
})
it('shows changed document name', function () {
cy.findAllByTestId('history-version-details')
.eq(1)
.within(() => {
cy.findByTestId('history-version-change-doc').should(
'have.text',
updates.updates[1].pathnames[0]
)
})
})
it('shows users', function () {
cy.findAllByTestId('history-version-details')
.eq(0)
.within(() => {
cy.findByTestId('history-version-metadata-users')
.should('contain.text', 'You')
.and('contain.text', updates.updates[0].meta.users[1].first_name)
})
})
})
describe('labels only', function () {
beforeEach(function () {
mountWithEditorProviders(<ChangeList />, scope, {
user: {
id: USER_ID,
email: USER_EMAIL,
isAdmin: true,
},
})
waitForData()
cy.findByLabelText(/labels/i).click({ force: true })
})
it('does not show the dropdown menu item for adding new labels', function () {
cy.findAllByTestId('history-version-details')
.eq(1)
.within(() => {
cy.findByRole('button', { name: /more actions/i }).click()
cy.findByRole('menu').within(() => {
cy.findByRole('menuitem', {
name: /label this version/i,
}).should('not.exist')
})
})
})
it('resets from compare to view mode when switching tabs', function () {
cy.findAllByTestId('history-version-details')
.eq(1)
.within(() => {
cy.findByRole('button', { name: /more actions/i }).click()
cy.findByRole('menu').within(() => {
cy.findByRole('menuitem', {
name: /compare to selected version/i,
}).click()
})
})
cy.wait('@diff')
cy.findByLabelText(/all history/i).click({ force: true })
cy.findAllByTestId('history-version-details').should($versions => {
const [first, ...rest] = Array.from($versions)
expect(first.dataset.selected === 'true').to.be.true
expect(rest.every(version => version.dataset.selected === 'false')).to
.be.true
})
})
})
describe('dropdown', function () {
beforeEach(function () {
mountWithEditorProviders(<ChangeList />, scope, {
user: {
id: USER_ID,
email: USER_EMAIL,
isAdmin: true,
},
})
waitForData()
})
it('adds badge/label', function () {
cy.findAllByTestId('history-version-details').eq(1).as('version')
cy.get('@version').within(() => {
cy.findByRole('button', { name: /more actions/i }).click()
cy.findByRole('menu').within(() => {
cy.findByRole('menuitem', {
name: /label this version/i,
}).click()
})
})
cy.findByRole('dialog').as('modal')
cy.intercept('POST', '/project/*/labels', req => {
req.reply(200, {
id: '64633ee158e9ef7da614c000',
comment: req.body.comment,
version: req.body.version,
user_id: USER_ID,
created_at: '2023-05-16T08:29:21.250Z',
user_display_name: 'john.doe',
})
}).as('addLabel')
const newLabel = 'my new label'
cy.get('@modal').within(() => {
cy.findByRole('heading', { name: /add label/i })
cy.findByRole('button', { name: /cancel/i })
cy.findByRole('button', { name: /add label/i }).should('be.disabled')
cy.findByPlaceholderText(/new label name/i).as('input')
cy.get('@input').type(newLabel)
cy.findByRole('button', { name: /add label/i }).should('be.enabled')
cy.get('@input').type('{enter}')
})
cy.wait('@addLabel')
cy.get('@version').within(() => {
cy.findAllByTestId('history-version-badge').should($badges => {
const includes = Array.from($badges).some(badge =>
badge.textContent?.includes(newLabel)
)
expect(includes).to.be.true
})
})
})
it('downloads version', function () {
cy.intercept('GET', '/project/*/version/*/zip', { statusCode: 200 }).as(
'download'
)
cy.findAllByTestId('history-version-details')
.eq(0)
.within(() => {
cy.findByRole('button', { name: /more actions/i }).click()
cy.findByRole('menu').within(() => {
cy.findByRole('menuitem', {
name: /download this version/i,
}).click()
})
})
cy.wait('@download')
})
it('compares versions', function () {
cy.findAllByTestId('history-version-details').should($versions => {
const [first, ...rest] = Array.from($versions)
expect(first).to.have.attr('data-selected', 'true')
rest.forEach(version =>
expect(version).to.have.attr('data-selected', 'false')
)
})
cy.intercept('GET', '/project/*/filetree/diff*', { statusCode: 200 }).as(
'compareDiff'
)
cy.findAllByTestId('history-version-details')
.last()
.within(() => {
cy.findByRole('button', { name: /more actions/i }).click()
cy.findByRole('menu').within(() => {
cy.findByRole('menuitem', {
name: /compare to selected version/i,
}).click()
})
})
cy.wait('@compareDiff')
})
})
describe('paywall', function () {

View file

@ -1,4 +1,4 @@
import { USER_ID } from '../../../helpers/editor-providers'
import { USER_ID, USER_EMAIL } from '../../../helpers/editor-providers'
export const updates = {
updates: [
@ -7,6 +7,12 @@ export const updates = {
toV: 4,
meta: {
users: [
{
first_name: 'testuser',
last_name: '',
email: USER_EMAIL,
id: USER_ID,
},
{
first_name: 'john.doe',
last_name: '',