Просмотр исходного кода

Merge pull request #8953 from overleaf/ae-pdf-synctex-tests

Fix and enable synctex controls tests

GitOrigin-RevId: 67174b79ed81cf67552fb63447b9c6aa0a99e9fd
Timothée Alby 4 лет назад
Родитель
Сommit
4171f9bd92

+ 143 - 74
services/web/test/frontend/components/pdf-preview/pdf-synctex-controls.spec.tsx

@@ -4,7 +4,6 @@ import { cloneDeep } from 'lodash'
 import { useDetachCompileContext as useCompileContext } from '../../../../frontend/js/shared/context/detach-compile-context'
 import { useDetachCompileContext as useCompileContext } from '../../../../frontend/js/shared/context/detach-compile-context'
 import { useFileTreeData } from '../../../../frontend/js/shared/context/file-tree-data-context'
 import { useFileTreeData } from '../../../../frontend/js/shared/context/file-tree-data-context'
 import { useEffect } from 'react'
 import { useEffect } from 'react'
-
 import { EditorProviders } from '../../helpers/editor-providers'
 import { EditorProviders } from '../../helpers/editor-providers'
 import { mockScope } from './scope'
 import { mockScope } from './scope'
 
 
@@ -54,6 +53,15 @@ const WithPosition = ({ mockPosition }: { mockPosition: Position }) => {
   return null
   return null
 }
 }
 
 
+// mock PDF scroll position update
+const setDetachedPosition = (mockPosition: Position) => {
+  sysendTestHelper.receiveMessage({
+    role: 'detacher',
+    event: 'state-position',
+    data: { value: mockPosition },
+  })
+}
+
 const WithSelectedEntities = ({
 const WithSelectedEntities = ({
   mockSelectedEntities = [],
   mockSelectedEntities = [],
 }: {
 }: {
@@ -67,22 +75,64 @@ const WithSelectedEntities = ({
 
 
   return null
   return null
 }
 }
+
+const interceptSyncCodeAsync = () => {
+  const output: { resolve: () => void } = {
+    resolve: () => {
+      // do nothing
+    },
+  }
+
+  cy.intercept('/project/*/sync/code?*', req => {
+    return new Promise(resolve => {
+      output.resolve = () => {
+        req.reply({
+          body: { pdf: cloneDeep(mockHighlights) },
+        })
+        resolve()
+      }
+    })
+  }).as('sync-code')
+
+  return output
+}
+
+const interceptSyncPdfAsync = () => {
+  const output: { resolve: () => void } = {
+    resolve: () => {
+      // do nothing
+    },
+  }
+
+  cy.intercept('/project/*/sync/pdf?*', req => {
+    return new Promise(resolve => {
+      output.resolve = () => {
+        req.reply({
+          body: { code: [{ file: 'main.tex', line: 100 }] },
+          delay: 1,
+        })
+        resolve()
+      }
+    })
+  }).as('sync-pdf')
+
+  return output
+}
+
+const interceptSyncPdf = () => {
+  cy.intercept('/project/*/sync/pdf?*', req => {
+    req.reply({
+      body: { code: [{ file: 'main.tex', line: 100 }] },
+    })
+  }).as('sync-pdf')
+}
+
 describe('<PdfSynctexControls/>', function () {
 describe('<PdfSynctexControls/>', function () {
   beforeEach(function () {
   beforeEach(function () {
     window.metaAttributesCache = new Map()
     window.metaAttributesCache = new Map()
 
 
     cy.interceptCompile()
     cy.interceptCompile()
     cy.interceptEvents()
     cy.interceptEvents()
-
-    cy.intercept('/project/*/sync/code?*', {
-      body: { pdf: cloneDeep(mockHighlights) },
-      delay: 100,
-    }).as('sync-code')
-
-    cy.intercept('/project/*/sync/pdf?*', {
-      body: { code: [{ file: 'main.tex', line: 100 }] },
-      delay: 100,
-    }).as('sync-pdf')
   })
   })
 
 
   afterEach(function () {
   afterEach(function () {
@@ -111,21 +161,29 @@ describe('<PdfSynctexControls/>', function () {
       )
       )
     })
     })
 
 
-    cy.get('body')
-      .findByRole('button', { name: 'Go to code location in PDF' })
-      .click()
-    cy.get('body')
-      .findByRole('button', { name: 'Go to code location in PDF' })
+    cy.wait('@compile').then(() => {
+      setDetachedPosition(mockPosition)
+    })
+
+    const syncCode = interceptSyncCodeAsync()
+
+    cy.findByRole('button', { name: 'Go to code location in PDF' }).click()
+    cy.findByRole('button', { name: 'Go to code location in PDF' })
       .should('be.disabled')
       .should('be.disabled')
+      .then(() => {
+        syncCode.resolve()
+      })
 
 
     cy.wait('@sync-code')
     cy.wait('@sync-code')
 
 
-    cy.get('body')
-      .findByRole('button', { name: /^Go to PDF location in code/ })
-      .click()
-    cy.get('body')
-      .findByRole('button', { name: /^Go to PDF location in code/ })
+    const syncPdf = interceptSyncPdfAsync()
+
+    cy.findByRole('button', { name: /^Go to PDF location in code/ }).click()
+    cy.findByRole('button', { name: /^Go to PDF location in code/ })
       .should('be.disabled')
       .should('be.disabled')
+      .then(() => {
+        syncPdf.resolve()
+      })
 
 
     cy.wait('@sync-pdf')
     cy.wait('@sync-pdf')
   })
   })
@@ -143,9 +201,9 @@ describe('<PdfSynctexControls/>', function () {
       </EditorProviders>
       </EditorProviders>
     )
     )
 
 
-    cy.get('body')
-      .findByRole('button', { name: 'Go to code location in PDF' })
-      .should('be.disabled')
+    cy.findByRole('button', { name: 'Go to code location in PDF' }).should(
+      'be.disabled'
+    )
   })
   })
 
 
   it('disables button when a file is selected', function () {
   it('disables button when a file is selected', function () {
@@ -159,9 +217,9 @@ describe('<PdfSynctexControls/>', function () {
       </EditorProviders>
       </EditorProviders>
     )
     )
 
 
-    cy.get('body')
-      .findByRole('button', { name: 'Go to code location in PDF' })
-      .should('be.disabled')
+    cy.findByRole('button', { name: 'Go to code location in PDF' }).should(
+      'be.disabled'
+    )
   })
   })
 
 
   describe('with detacher role', function () {
   describe('with detacher role', function () {
@@ -180,15 +238,14 @@ describe('<PdfSynctexControls/>', function () {
         </EditorProviders>
         </EditorProviders>
       )
       )
 
 
-      cy.get('body')
-        .findByRole('button', { name: /^Go to PDF location in code/ })
-        .should('not.exist')
+      cy.findByRole('button', { name: /^Go to PDF location in code/ }).should(
+        'not.exist'
+      )
 
 
       cy.get('.synctex-control-icon').should('not.exist')
       cy.get('.synctex-control-icon').should('not.exist')
     })
     })
 
 
-    // eslint-disable-next-line mocha/no-skipped-tests
-    it.skip('send set highlights action', function () {
+    it('send set highlights action', function () {
       const scope = mockScope()
       const scope = mockScope()
 
 
       cy.mount(
       cy.mount(
@@ -212,6 +269,8 @@ describe('<PdfSynctexControls/>', function () {
         )
         )
       })
       })
 
 
+      const syncing = interceptSyncCodeAsync()
+
       cy.findByRole('button', {
       cy.findByRole('button', {
         name: 'Go to code location in PDF',
         name: 'Go to code location in PDF',
       })
       })
@@ -220,15 +279,19 @@ describe('<PdfSynctexControls/>', function () {
 
 
       cy.findByRole('button', {
       cy.findByRole('button', {
         name: 'Go to code location in PDF',
         name: 'Go to code location in PDF',
-      }).should('be.disabled')
+      })
+        .should('be.disabled')
+        .then(() => {
+          syncing.resolve()
+        })
 
 
-      cy.wait('@sync-code').should(() => {
-        const messages = sysendTestHelper
-          .getAllBroacastMessages()
-          .map((item: any) => item.args[1])
+      cy.wait('@sync-code')
 
 
-        const message = messages.find(
-          (message: any) => message.event === 'action-setHighlights'
+      cy.findByRole('button', {
+        name: 'Go to code location in PDF',
+      }).should(() => {
+        const message = sysendTestHelper.getMessageWithEvent(
+          'action-setHighlights'
         )
         )
 
 
         // synctex is called locally and the result are broadcast for the detached tab
         // synctex is called locally and the result are broadcast for the detached tab
@@ -242,6 +305,8 @@ describe('<PdfSynctexControls/>', function () {
     })
     })
 
 
     it('reacts to sync to code action', function () {
     it('reacts to sync to code action', function () {
+      interceptSyncPdf()
+
       const scope = mockScope()
       const scope = mockScope()
 
 
       cy.mount(
       cy.mount(
@@ -250,9 +315,7 @@ describe('<PdfSynctexControls/>', function () {
           <WithSelectedEntities mockSelectedEntities={mockSelectedEntities} />
           <WithSelectedEntities mockSelectedEntities={mockSelectedEntities} />
           <PdfSynctexControls />
           <PdfSynctexControls />
         </EditorProviders>
         </EditorProviders>
-      )
-
-      cy.wait('@compile').then(() => {
+      ).then(() => {
         sysendTestHelper.receiveMessage({
         sysendTestHelper.receiveMessage({
           role: 'detached',
           role: 'detached',
           event: 'action-sync-to-code',
           event: 'action-sync-to-code',
@@ -288,54 +351,60 @@ describe('<PdfSynctexControls/>', function () {
       cy.get('.synctex-control-icon').should('not.exist')
       cy.get('.synctex-control-icon').should('not.exist')
     })
     })
 
 
-    // eslint-disable-next-line mocha/no-skipped-tests
-    it.skip('send go to code line action', function () {
+    it('send go to code line action', function () {
       const scope = mockScope()
       const scope = mockScope()
 
 
       cy.mount(
       cy.mount(
         <EditorProviders scope={scope}>
         <EditorProviders scope={scope}>
-          <WithPosition mockPosition={mockPosition} />
           <PdfSynctexControls />
           <PdfSynctexControls />
         </EditorProviders>
         </EditorProviders>
       )
       )
 
 
-      cy.wait('@compile')
+      cy.wait('@compile').then(() => {
+        sysendTestHelper.receiveMessage({
+          role: 'detacher',
+          event: `state-position`,
+          data: { value: mockPosition },
+        })
+      })
 
 
-      cy.get('body').findByRole('button', {
+      cy.findByRole('button', {
         name: /^Go to PDF location in code/,
         name: /^Go to PDF location in code/,
       })
       })
 
 
-      cy.get('body')
-        .findByRole('button', { name: /^Go to PDF location in code/ })
+      cy.findByRole('button', { name: /^Go to PDF location in code/ })
         .should('not.be.disabled')
         .should('not.be.disabled')
         .then(() => {
         .then(() => {
           sysendTestHelper.resetHistory()
           sysendTestHelper.resetHistory()
         })
         })
 
 
-      cy.get('body')
-        .findByRole('button', { name: /^Go to PDF location in code/ })
-        .click()
+      cy.findByRole('button', { name: /^Go to PDF location in code/ }).click()
 
 
       // the button is only disabled when the state is updated via sysend
       // the button is only disabled when the state is updated via sysend
-      cy.get('body')
-        .findByRole('button', { name: /^Go to PDF location in code/ })
-        .should('not.be.disabled')
+      cy.findByRole('button', { name: /^Go to PDF location in code/ }).should(
+        'not.be.disabled'
+      )
 
 
-      cy.get('.synctex-spin-icon')
-        .should('not.exist')
-        .then(() => {
-          expect(sysendTestHelper.getLastBroacastMessage()).to.deep.equal({
+      cy.get('.synctex-spin-icon').should('not.exist')
+
+      cy.findByRole('button', { name: /^Go to PDF location in code/ }).should(
+        () => {
+          const message = sysendTestHelper.getMessageWithEvent(
+            'action-sync-to-code'
+          )
+
+          expect(message).to.deep.equal({
             role: 'detached',
             role: 'detached',
             event: 'action-sync-to-code',
             event: 'action-sync-to-code',
             data: {
             data: {
               args: [mockPosition, 72],
               args: [mockPosition, 72],
             },
             },
           })
           })
-        })
+        }
+      )
     })
     })
 
 
-    // eslint-disable-next-line mocha/no-skipped-tests
-    it.skip('update inflight state', function () {
+    it('update inflight state', function () {
       const scope = mockScope()
       const scope = mockScope()
 
 
       cy.mount(
       cy.mount(
@@ -345,15 +414,15 @@ describe('<PdfSynctexControls/>', function () {
         </EditorProviders>
         </EditorProviders>
       ).then(() => {
       ).then(() => {
         sysendTestHelper.receiveMessage({
         sysendTestHelper.receiveMessage({
-          role: 'detached',
-          event: 'state-has-single-selected-doc',
-          data: { value: true },
+          role: 'detacher',
+          event: `state-position`,
+          data: { value: mockPosition },
         })
         })
       })
       })
 
 
-      cy.get('body')
-        .findByRole('button', { name: /^Go to PDF location in code/ })
-        .should('be.disabled')
+      cy.findByRole('button', { name: /^Go to PDF location in code/ }).should(
+        'not.be.disabled'
+      )
 
 
       cy.get('.synctex-spin-icon')
       cy.get('.synctex-spin-icon')
         .should('not.exist')
         .should('not.exist')
@@ -365,9 +434,9 @@ describe('<PdfSynctexControls/>', function () {
           })
           })
         })
         })
 
 
-      cy.get('body')
-        .findByRole('button', { name: /^Go to PDF location in code/ })
-        .should('be.disabled')
+      cy.findByRole('button', { name: /^Go to PDF location in code/ }).should(
+        'be.disabled'
+      )
 
 
       cy.get('.synctex-spin-icon')
       cy.get('.synctex-spin-icon')
         .should('have.length', 1)
         .should('have.length', 1)
@@ -379,9 +448,9 @@ describe('<PdfSynctexControls/>', function () {
           })
           })
         })
         })
 
 
-      cy.get('body')
-        .findByRole('button', { name: /^Go to PDF location in code/ })
-        .should('not.be.disabled')
+      cy.findByRole('button', { name: /^Go to PDF location in code/ }).should(
+        'not.be.disabled'
+      )
 
 
       cy.get('.synctex-spin-icon').should('not.exist')
       cy.get('.synctex-spin-icon').should('not.exist')
     })
     })

+ 8 - 1
services/web/test/frontend/helpers/sysend.js

@@ -32,7 +32,13 @@ function getAllBroacastMessages() {
 // this fakes receiving a message by calling the handler add to `on`. A bit
 // this fakes receiving a message by calling the handler add to `on`. A bit
 // funky, but works for now
 // funky, but works for now
 function receiveMessage(message) {
 function receiveMessage(message) {
-  getLastDetachCall('on')?.args[1](message)
+  getLastDetachCall('on').args[1](message)
+}
+
+function getMessageWithEvent(eventName) {
+  return getAllBroacastMessages()
+    .map(item => item.args[1])
+    .find(item => item.event === eventName)
 }
 }
 
 
 export default {
 export default {
@@ -42,5 +48,6 @@ export default {
   getLastDetachCall,
   getLastDetachCall,
   getLastBroacastMessage,
   getLastBroacastMessage,
   getAllBroacastMessages,
   getAllBroacastMessages,
+  getMessageWithEvent,
   receiveMessage,
   receiveMessage,
 }
 }