Skip to content

Commit 83ffb07

Browse files
authored
test: run app e2e tests in Windows CI (#19979)
1 parent bb37389 commit 83ffb07

File tree

12 files changed

+101
-99
lines changed

12 files changed

+101
-99
lines changed

circle.yml

+21-9
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ mainBuildFilters: &mainBuildFilters
2929
only:
3030
- develop
3131
- 10.0-release
32+
- unify-1001-windows-app-e2e-tests
3233

3334
# usually we don't build Mac app - it takes a long time
3435
# but sometimes we want to really confirm we are doing the right thing
@@ -38,7 +39,7 @@ macWorkflowFilters: &mac-workflow-filters
3839
or:
3940
- equal: [ develop, << pipeline.git.branch >> ]
4041
- equal: [ '10.0-release', << pipeline.git.branch >> ]
41-
- equal: [ renovate/cypress-request-2.x, << pipeline.git.branch >> ]
42+
- equal: [ unify-1001-windows-app-e2e-tests, << pipeline.git.branch >> ]
4243
- matches:
4344
pattern: "-release$"
4445
value: << pipeline.git.branch >>
@@ -48,8 +49,7 @@ windowsWorkflowFilters: &windows-workflow-filters
4849
or:
4950
- equal: [ develop, << pipeline.git.branch >> ]
5051
- equal: [ '10.0-release', << pipeline.git.branch >> ]
51-
- equal: [ test-binary-downstream-windows, << pipeline.git.branch >> ]
52-
- equal: [ unify-890-ebusy-eperm, << pipeline.git.branch >> ]
52+
- equal: [ unify-1001-windows-app-e2e-tests, << pipeline.git.branch >> ]
5353
- matches:
5454
pattern: "-release$"
5555
value: << pipeline.git.branch >>
@@ -450,14 +450,19 @@ commands:
450450
default: ''
451451
steps:
452452
- restore_cached_workspace
453+
- run:
454+
# TODO: How can we have preinstalled browsers on CircleCI?
455+
name: 'Install Chrome on Windows'
456+
command: |
457+
[[ $PLATFORM == 'windows' && '<<parameters.browser>>' == 'chrome' ]] && choco install googlechrome || true
453458
- run:
454459
command: |
455460
cmd=$([[ <<parameters.percy>> == 'true' ]] && echo 'yarn percy exec --parallel -- --') || true
456461
DEBUG=<<parameters.debug>> \
457462
CYPRESS_INTERNAL_FORCE_BROWSER_RELAUNCH='true' \
458463
CYPRESS_KONFIG_ENV=production \
459464
CYPRESS_RECORD_KEY=$TEST_LAUNCHPAD_RECORD_KEY \
460-
PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \
465+
PERCY_PARALLEL_NONCE=$PLATFORM-$CIRCLE_SHA1 \
461466
PERCY_ENABLE=${PERCY_TOKEN:-0} \
462467
PERCY_PARALLEL_TOTAL=-1 \
463468
$cmd yarn workspace @packages/<<parameters.package>> cypress:run:<<parameters.type>> --browser <<parameters.browser>> --record --parallel --group <<parameters.package>>-<<parameters.type>>
@@ -1029,7 +1034,7 @@ jobs:
10291034
echo "This is an external PR, cannot access other services"
10301035
circleci-agent step halt
10311036
fi
1032-
- run: PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 yarn percy build:finalize
1037+
- run: PERCY_PARALLEL_NONCE=$PLATFORM-$CIRCLE_SHA1 yarn percy build:finalize
10331038

10341039
cli-visual-tests:
10351040
<<: *defaults
@@ -1050,7 +1055,7 @@ jobs:
10501055
- run:
10511056
name: Upload CLI snapshots for diffing
10521057
command: |
1053-
PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \
1058+
PERCY_PARALLEL_NONCE=$PLATFORM-$CIRCLE_SHA1 \
10541059
PERCY_ENABLE=${PERCY_TOKEN:-0} \
10551060
PERCY_PARALLEL_TOTAL=-1 \
10561061
yarn percy snapshot ./cli/visual-snapshots
@@ -1308,7 +1313,7 @@ jobs:
13081313
command: |
13091314
CYPRESS_KONFIG_ENV=production \
13101315
CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \
1311-
PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \
1316+
PERCY_PARALLEL_NONCE=$PLATFORM-$CIRCLE_SHA1 \
13121317
PERCY_ENABLE=${PERCY_TOKEN:-0} \
13131318
PERCY_PARALLEL_TOTAL=-1 \
13141319
yarn percy exec --parallel -- -- \
@@ -1450,7 +1455,7 @@ jobs:
14501455
# will use PERCY_TOKEN environment variable if available
14511456
command: |
14521457
CYPRESS_KONFIG_ENV=production \
1453-
PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \
1458+
PERCY_PARALLEL_NONCE=$PLATFORM-$CIRCLE_SHA1 \
14541459
PERCY_ENABLE=${PERCY_TOKEN:-0} \
14551460
PERCY_PARALLEL_TOTAL=-1 \
14561461
yarn percy exec --parallel -- -- \
@@ -1568,7 +1573,7 @@ jobs:
15681573
- run:
15691574
name: Check current branch to persist artifacts
15701575
command: |
1571-
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "10.0-release" ]]; then
1576+
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "unify-1001-windows-app-e2e-tests" && "$CIRCLE_BRANCH" != "10.0-release" ]]; then
15721577
echo "Not uploading artifacts or posting install comment for this branch."
15731578
circleci-agent step halt
15741579
fi
@@ -2434,6 +2439,13 @@ windows-workflow: &windows-workflow
24342439
requires:
24352440
- windows-node-modules-install
24362441

2442+
- run-app-integration-tests-chrome:
2443+
name: windows-run-app-integration-tests-chrome
2444+
executor: windows
2445+
context: test-runner:launchpad-tests
2446+
requires:
2447+
- windows-build
2448+
24372449
- lint:
24382450
name: windows-lint
24392451
executor: windows

packages/app/cypress/e2e/index.cy.ts

+46-32
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
import defaultMessages from '@packages/frontend-shared/src/locales/en-US.json'
22

3+
function getPathForPlatform (posixPath: string) {
4+
if (Cypress.platform === 'win32') return posixPath.replaceAll('/', '\\')
5+
6+
return posixPath
7+
}
8+
9+
function getRunnerHref (specPath: string) {
10+
specPath = getPathForPlatform(specPath)
11+
12+
if (Cypress.platform === 'win32') specPath = specPath.replaceAll('\\', '%5C')
13+
14+
return `#/specs/runner?file=${specPath}`
15+
}
16+
317
describe('App: Index', () => {
418
describe('Testing Type: E2E', () => {
519
context('project with default spec pattern', () => {
@@ -89,7 +103,7 @@ describe('App: Index', () => {
89103
'waiting',
90104
'window',
91105
].map((file) => `cypress/e2e/2-advanced-examples/${file}.cy.js`)),
92-
]
106+
].map(getPathForPlatform)
93107

94108
it('scaffolds example files when card is clicked', () => {
95109
cy.get('@ScaffoldCard').click()
@@ -156,20 +170,20 @@ describe('App: Index', () => {
156170
cy.findAllByLabelText(defaultMessages.createSpec.e2e.importEmptySpec.inputPlaceholder)
157171
.as('enterSpecInput')
158172

159-
cy.get('@enterSpecInput').invoke('val').should('eq', 'cypress/e2e/filename.cy.js')
173+
cy.get('@enterSpecInput').invoke('val').should('eq', getPathForPlatform('cypress/e2e/filename.cy.js'))
160174
cy.contains(defaultMessages.createSpec.e2e.importEmptySpec.invalidSpecWarning).should('not.exist')
161175
cy.get('@enterSpecInput').clear()
162176
cy.contains(defaultMessages.createSpec.e2e.importEmptySpec.invalidSpecWarning).should('not.exist')
163177

164178
// Shows entered file does not match spec pattern
165-
cy.get('@enterSpecInput').type('cypress/e2e/no-match')
179+
cy.get('@enterSpecInput').type(getPathForPlatform('cypress/e2e/no-match'))
166180
cy.contains(defaultMessages.createSpec.e2e.importEmptySpec.invalidSpecWarning)
167181
cy.contains('button', defaultMessages.createSpec.createSpec).should('be.disabled')
168182

169183
//Shows extension warning
170-
cy.get('@enterSpecInput').clear().type('cypress/e2e/MyTest.spec.j')
184+
cy.get('@enterSpecInput').clear().type(getPathForPlatform('cypress/e2e/MyTest.spec.j'))
171185
cy.intercept('mutation-EmptyGenerator_MatchSpecFile', (req) => {
172-
if (req.body.variables.specFile === 'cypress/e2e/MyTest.spec.jx') {
186+
if (req.body.variables.specFile === getPathForPlatform('cypress/e2e/MyTest.spec.jx')) {
173187
req.on('before:response', (res) => {
174188
res.body.data.matchesSpecPattern = true
175189
})
@@ -181,11 +195,11 @@ describe('App: Index', () => {
181195
cy.contains('span', '{filename}.cy.jx')
182196

183197
// Create spec
184-
cy.get('@enterSpecInput').clear().type('cypress/e2e/MyTest.cy.js')
198+
cy.get('@enterSpecInput').clear().type(getPathForPlatform('cypress/e2e/MyTest.cy.js'))
185199
cy.contains('button', defaultMessages.createSpec.createSpec).should('not.be.disabled').click()
186200
cy.contains('h2', defaultMessages.createSpec.successPage.header)
187201

188-
cy.get('[data-cy="file-row"]').contains('cypress/e2e/MyTest.cy.js').click()
202+
cy.get('[data-cy="file-row"]').contains(getPathForPlatform('cypress/e2e/MyTest.cy.js')).click()
189203

190204
// TODO: code rendering is flaky in CI
191205
// cy.get('code').should('contain', 'describe(\'MyTest.cy.js\'')
@@ -279,11 +293,11 @@ describe('App: Index', () => {
279293
it('should create example files on an empty project', () => {
280294
cy.contains('[data-cy="card"]', defaultMessages.createSpec.e2e.importFromScaffold.header).click()
281295
// TODO: Check that the popup stays open
282-
cy.withCtx(async (ctx) => {
283-
const stats = await ctx.actions.file.checkIfFileExists('cypress/e2e/1-getting-started/todo.cy.js')
296+
cy.withCtx(async (ctx, o) => {
297+
const stats = await ctx.actions.file.checkIfFileExists(o.path)
284298

285299
expect(stats?.isFile()).to.be.true
286-
})
300+
}, { path: getPathForPlatform('cypress/e2e/1-getting-started/todo.cy.js') })
287301
})
288302
})
289303
})
@@ -440,10 +454,10 @@ describe('App: Index', () => {
440454
}).as('SuccessDialog').within(() => {
441455
cy.validateExternalLink({ name: 'Need help', href: 'https://on.cypress.io' })
442456
cy.findByRole('button', { name: 'Close' }).should('be.visible')
443-
cy.contains('src/stories/Button.stories.cy.jsx').should('be.visible')
457+
cy.contains(getPathForPlatform('src/stories/Button.stories.cy.jsx')).should('be.visible')
444458

445459
cy.findByRole('link', { name: 'Okay, run the spec' })
446-
.should('have.attr', 'href', '#/specs/runner?file=src/stories/Button.stories.cy.jsx')
460+
.should('have.attr', 'href', getRunnerHref('src/stories/Button.stories.cy.jsx'))
447461

448462
cy.findByRole('button', { name: 'Create another spec' }).click()
449463
})
@@ -596,11 +610,11 @@ describe('App: Index', () => {
596610
name: defaultMessages.createSpec.successPage.header,
597611
}).as('SuccessDialog').within(() => {
598612
cy.validateExternalLink({ name: 'Need help', href: 'https://on.cypress.io' })
599-
cy.contains('src/App.cy.jsx').should('be.visible')
613+
cy.contains(getPathForPlatform('src/App.cy.jsx')).should('be.visible')
600614
cy.findByRole('button', { name: 'Close' }).should('be.visible')
601615

602616
cy.findByRole('link', { name: 'Okay, run the spec' })
603-
.should('have.attr', 'href', '#/specs/runner?file=src/App.cy.jsx')
617+
.should('have.attr', 'href', getRunnerHref('src/App.cy.jsx'))
604618

605619
cy.findByRole('button', { name: 'Create another spec' }).click()
606620
})
@@ -626,7 +640,7 @@ describe('App: Index', () => {
626640
cy.findByRole('dialog', { name: defaultMessages.createSpec.successPage.header }).as('SuccessDialog').within(() => {
627641
cy.findByRole('link', {
628642
name: 'Okay, run the spec',
629-
}).should('have.attr', 'href', '#/specs/runner?file=src/App.cy.jsx').click()
643+
}).should('have.attr', 'href', getRunnerHref('src/App.cy.jsx')).click()
630644
})
631645

632646
cy.findByTestId('spec-gen-component-app', { timeout: 5000 }).should('be.visible')
@@ -713,7 +727,7 @@ describe('App: Index', () => {
713727

714728
cy.get('[data-cy=file-list-row]').first().click()
715729

716-
cy.get('input').invoke('val').should('eq', 'src/App.cy.jsx')
730+
cy.get('input').invoke('val').should('eq', getPathForPlatform('src/App.cy.jsx'))
717731
cy.contains(defaultMessages.createSpec.component.importEmptySpec.header)
718732

719733
cy.contains(defaultMessages.components.button.cancel).click()
@@ -731,23 +745,23 @@ describe('App: Index', () => {
731745

732746
cy.get('[data-cy=file-list-row]').first().click()
733747

734-
cy.get('input').invoke('val').should('eq', 'src/App.cy.jsx')
748+
cy.get('input').invoke('val').should('eq', getPathForPlatform('src/App.cy.jsx'))
735749
cy.contains(defaultMessages.createSpec.component.importEmptySpec.header)
736750
cy.contains(defaultMessages.createSpec.component.importEmptySpec.invalidComponentWarning)
737751
cy.get('input').clear()
738752
cy.contains(defaultMessages.createSpec.component.importEmptySpec.invalidComponentWarning).should('not.exist')
739753
cy.contains('button', defaultMessages.createSpec.createSpec).should('be.disabled')
740754

741-
cy.get('input').clear().type('src/specs-folder/MyTest.cy.jsx')
755+
cy.get('input').clear().type(getPathForPlatform('src/specs-folder/MyTest.cy.jsx'))
742756
cy.contains('button', defaultMessages.createSpec.createSpec).should('not.be.disabled').click()
743757
cy.contains('h2', defaultMessages.createSpec.successPage.header)
744758

745-
cy.get('[data-cy="file-row"]').contains('src/specs-folder/MyTest.cy.jsx').click()
759+
cy.get('[data-cy="file-row"]').contains(getPathForPlatform('src/specs-folder/MyTest.cy.jsx')).click()
746760

747761
cy.findByRole('dialog', { name: defaultMessages.createSpec.successPage.header }).as('SuccessDialog').within(() => {
748762
cy.findByRole('link', {
749763
name: 'Okay, run the spec',
750-
}).should('have.attr', 'href', '#/specs/runner?file=src/specs-folder/MyTest.cy.jsx')
764+
}).should('have.attr', 'href', getRunnerHref('src/specs-folder/MyTest.cy.jsx'))
751765
})
752766
})
753767

@@ -761,23 +775,23 @@ describe('App: Index', () => {
761775

762776
cy.get('[data-cy=file-list-row]').first().click()
763777

764-
cy.get('input').invoke('val').should('eq', 'src/stories/Button.stories.cy.jsx')
778+
cy.get('input').invoke('val').should('eq', getPathForPlatform('src/stories/Button.stories.cy.jsx'))
765779
cy.contains(defaultMessages.createSpec.component.importEmptySpec.header)
766780
cy.contains(defaultMessages.createSpec.component.importEmptySpec.invalidComponentWarning)
767781
cy.get('input').clear()
768782
cy.contains(defaultMessages.createSpec.component.importEmptySpec.invalidComponentWarning).should('not.exist')
769783
cy.contains('button', defaultMessages.createSpec.createSpec).should('be.disabled')
770784

771-
cy.get('input').clear().type('src/specs-folder/Button.stories.cy.jsx')
785+
cy.get('input').clear().type(getPathForPlatform('src/specs-folder/Button.stories.cy.jsx'))
772786
cy.contains('button', defaultMessages.createSpec.createSpec).should('not.be.disabled').click()
773787
cy.contains('h2', defaultMessages.createSpec.successPage.header)
774788

775-
cy.get('[data-cy="file-row"]').contains('src/specs-folder/Button.stories.cy.jsx').click()
789+
cy.get('[data-cy="file-row"]').contains(getPathForPlatform('src/specs-folder/Button.stories.cy.jsx')).click()
776790

777791
cy.findByRole('dialog', { name: defaultMessages.createSpec.successPage.header }).as('SuccessDialog').within(() => {
778792
cy.findByRole('link', {
779793
name: 'Okay, run the spec',
780-
}).should('have.attr', 'href', '#/specs/runner?file=src/specs-folder/Button.stories.cy.jsx')
794+
}).should('have.attr', 'href', getRunnerHref('src/specs-folder/Button.stories.cy.jsx'))
781795
})
782796
})
783797
})
@@ -821,15 +835,15 @@ describe('App: Index', () => {
821835
.clear().type(componentGlob, { parseSpecialCharSequences: false })
822836

823837
cy.contains('Button.jsx').click()
824-
cy.findByTestId('file-row').contains('src/stories/Button.cy.js').click()
838+
cy.findByTestId('file-row').contains(getPathForPlatform('src/stories/Button.cy.js')).click()
825839

826-
cy.withCtx(async (ctx) => {
840+
cy.withCtx(async (ctx, o) => {
827841
const spec = (
828842
await ctx.project.findSpecs(ctx.currentProject ?? '', 'component', ['**/*.cy.jsx'], [], [])
829-
).find((spec) => spec.relative === 'src/stories/Button.cy.jsx')
843+
).find((spec) => spec.relative === o.path)
830844

831845
expect(spec).to.exist
832-
})
846+
}, { path: getPathForPlatform('src/stories/Button.cy.jsx') })
833847
})
834848

835849
it('should generate spec from story', () => {
@@ -842,15 +856,15 @@ describe('App: Index', () => {
842856
checkCodeGenCandidates(['Button.stories.jsx'])
843857

844858
cy.contains('Button.stories.jsx').click()
845-
cy.findByTestId('file-row').contains('src/stories/Button.stories.cy.js').click()
859+
cy.findByTestId('file-row').contains(getPathForPlatform('src/stories/Button.stories.cy.js')).click()
846860
cy.contains('composeStories')
847861

848-
cy.withCtx(async (ctx) => {
862+
cy.withCtx(async (ctx, o) => {
849863
const spec = (await ctx.project.findSpecs(ctx.currentProject ?? '', 'component', ['**/*.cy.jsx'], [], []))
850-
.find((spec) => spec.relative === 'src/stories/Button.stories.cy.jsx')
864+
.find((spec) => spec.relative === o.path)
851865

852866
expect(spec).to.exist
853-
})
867+
}, { path: getPathForPlatform('src/stories/Button.stories.cy.jsx') })
854868
})
855869
})
856870
})

packages/app/cypress/e2e/runs.cy.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,11 @@ describe('App: Runs', { viewportWidth: 1200 }, () => {
321321
cy.get('@firstRun').get('[data-cy="run-card-author"]').contains('John Appleseed')
322322
cy.get('@firstRun').get('[data-cy="run-card-avatar')
323323
cy.get('@firstRun').get('[data-cy="run-card-branch"]').contains('main')
324-
cy.get('@firstRun').contains(`3:17`)
324+
325+
// the exact timestamp string will depend on the user's browser's locale settings
326+
const localeTimeString = (new Date('2022-02-02T08:17:00.005Z')).toLocaleTimeString()
327+
328+
cy.get('@firstRun').contains(localeTimeString)
325329
cy.get('@firstRun').contains('span', 'skipped')
326330
cy.get('@firstRun').get('span').contains('pending')
327331
cy.get('@firstRun').get('span').contains('passed')

packages/app/cypress/e2e/settings.cy.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,11 @@ describe('App: Settings without cloud', () => {
147147
cy.findByText('Project Settings').click()
148148

149149
cy.get('[data-cy=config-code]').within(() => {
150-
cy.contains('browsers: chrome, firefox')
150+
const { browsers } = Cypress.config()
151+
152+
expect(browsers).to.have.length.greaterThan(1)
153+
154+
cy.contains(`browsers: ${browsers.filter((b) => b.name !== 'electron').map((b) => b.name).join(', ')}`)
151155
})
152156
})
153157
})

packages/app/cypress/e2e/sidebar_navigation.cy.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -88,20 +88,20 @@ describe('Sidebar Navigation', () => {
8888

8989
cy.findByLabelText('Sidebar').closest('[aria-expanded]').should('have.attr', 'aria-expanded', 'false')
9090

91-
cy.get('[data-cy="sidebar-header"').realHover()
91+
cy.get('[data-cy="sidebar-header"').trigger('mouseenter')
9292
cy.contains('#tooltip-target > div', 'todos')
9393
cy.percySnapshot()
9494
cy.get('[data-cy="sidebar-header"]').trigger('mouseout')
9595

96-
cy.get('[data-e2e-href="/runs"]').realHover()
96+
cy.get('[data-e2e-href="/runs"]').trigger('mouseenter')
9797
cy.contains('#tooltip-target > div', 'Runs')
9898
cy.get('[data-e2e-href="/runs"]').trigger('mouseout')
9999

100-
cy.get('[data-e2e-href="/specs"]').realHover()
100+
cy.get('[data-e2e-href="/specs"]').trigger('mouseenter')
101101
cy.contains('#tooltip-target > div', 'Specs')
102102
cy.get('[data-e2e-href="/specs"]').trigger('mouseout')
103103

104-
cy.get('[data-e2e-href="/settings"]').realHover()
104+
cy.get('[data-e2e-href="/settings"]').trigger('mouseenter')
105105
cy.contains('#tooltip-target > div', 'Settings')
106106
cy.get('[data-e2e-href="/settings"]').trigger('mouseout')
107107
})

packages/app/src/paths.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export function getPathForPlatform (posixPath: string) {
2+
if (Cypress.platform === 'win32') return posixPath.replaceAll('/', '\\')
3+
4+
return posixPath
5+
}

0 commit comments

Comments
 (0)