diff --git a/.eslintignore b/.eslintignore index 514583fc1e..63bb87eaf8 100644 --- a/.eslintignore +++ b/.eslintignore @@ -74,6 +74,8 @@ npm/cypress-schematic/src/**/*.js /npm/vue2/**/*.vue packages/data-context/test/unit/codegen/files +packages/config/test/__fixtures__/**/* +packages/config/test/__babel_fixtures__/**/* # community templates we test against, no need to lint system-tests/projects/cra-4/**/* diff --git a/circle.yml b/circle.yml index 99001dd0c6..6fe2c9a45e 100644 --- a/circle.yml +++ b/circle.yml @@ -29,7 +29,7 @@ mainBuildFilters: &mainBuildFilters only: - develop - 10.0-release - - chore/cutover-to-bundled-react-mount + - new-cmd-log-styles # uncomment & add to the branch conditions below to disable the main linux # flow if we don't want to test it for a certain branch @@ -49,9 +49,7 @@ macWorkflowFilters: &mac-workflow-filters or: - equal: [ develop, << pipeline.git.branch >> ] - equal: [ '10.0-release', << pipeline.git.branch >> ] - - equal: [ chore/cutover-to-bundled-react-mount, << pipeline.git.branch >> ] - - equal: [ feature-multidomain, << pipeline.git.branch >> ] - - equal: [ unify-1449-beta-slug-length, << pipeline.git.branch >> ] + - equal: [ new-cmd-log-styles, << pipeline.git.branch >> ] - matches: pattern: "-release$" value: << pipeline.git.branch >> @@ -114,6 +112,16 @@ executors: PLATFORM: windows commands: + verify_should_persist_artifacts: + steps: + - run: + name: Check current branch to persist artifacts + command: | + if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "new-cmd-log-styles" && "$CIRCLE_BRANCH" != "10.0-release" ]]; then + echo "Not uploading artifacts or posting install comment for this branch." + circleci-agent step halt + fi + restore_workspace_binaries: steps: - attach_workspace: @@ -1773,13 +1781,7 @@ jobs: - build-binary - build-cypress-npm-package: executor: << parameters.executor >> - - run: - name: Check current branch to persist artifacts - command: | - if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "chore/cutover-to-bundled-react-mount" && "$CIRCLE_BRANCH" != "unify-1036-windows-test-projects" && "$CIRCLE_BRANCH" != "10.0-release" && "$CIRCLE_BRANCH" != "unify-1449-beta-slug-length" && "$CIRCLE_BRANCH" != "feature-multidomain" ]]; then - echo "Not uploading artifacts or posting install comment for this branch." - circleci-agent step halt - fi + - verify_should_persist_artifacts - upload-build-artifacts - post-install-comment diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index ba00152589..e0bc5718a5 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2983,7 +2983,7 @@ declare namespace Cypress { xhrUrl: string } - interface TestConfigOverrides extends Partial> { + interface TestConfigOverrides extends Partial> { browser?: IsBrowserMatcher | IsBrowserMatcher[] keystrokeDelay?: number } diff --git a/npm/vite-dev-server/client/initCypressTests.js b/npm/vite-dev-server/client/initCypressTests.js index 3d38b93884..801a282065 100644 --- a/npm/vite-dev-server/client/initCypressTests.js +++ b/npm/vite-dev-server/client/initCypressTests.js @@ -13,13 +13,15 @@ const supportFile = CypressInstance.config('supportFile') const projectRoot = CypressInstance.config('projectRoot') const devServerPublicPathRoute = CypressInstance.config('devServerPublicPathRoute') -let supportRelativeToProjectRoot = supportFile.replace(projectRoot, '') - -if (CypressInstance.config('platform') === 'win32') { - supportRelativeToProjectRoot = supportFile.replace(projectRoot.replaceAll('/', '\\')) -} - if (supportFile) { + let supportRelativeToProjectRoot = supportFile.replace(projectRoot, '') + + if (CypressInstance.config('platform') === 'win32') { + const platformProjectRoot = projectRoot.replaceAll('/', '\\') + + supportRelativeToProjectRoot = supportFile.replace(platformProjectRoot, '') + } + // We need a slash before /cypress/supportFile.js, this happens by default // with the current string replacement logic. importsToLoad.push(() => import(`${devServerPublicPathRoute}${supportRelativeToProjectRoot}`)) diff --git a/npm/vite-dev-server/cypress/e2e/vite-dev-server.cy.ts b/npm/vite-dev-server/cypress/e2e/vite-dev-server.cy.ts new file mode 100644 index 0000000000..7ec6dfdba2 --- /dev/null +++ b/npm/vite-dev-server/cypress/e2e/vite-dev-server.cy.ts @@ -0,0 +1,11 @@ +describe('Config options', () => { + it('supports supportFile = false', () => { + cy.scaffoldProject('vite2.9.1-react') + cy.openProject('vite2.9.1-react', ['--config-file', 'cypress-vite-no-support.config.ts']) + cy.startAppServer('component') + + cy.visitApp() + cy.contains('App.cy.jsx').click() + cy.get('.passed > .num').should('contain', 1) + }) +}) diff --git a/npm/webpack-dev-server/cypress/e2e/webpack-dev-server.cy.ts b/npm/webpack-dev-server/cypress/e2e/webpack-dev-server.cy.ts new file mode 100644 index 0000000000..d888a08608 --- /dev/null +++ b/npm/webpack-dev-server/cypress/e2e/webpack-dev-server.cy.ts @@ -0,0 +1,11 @@ +describe('Config options', () => { + it('supports supportFile = false', () => { + cy.scaffoldProject('webpack5_wds4-react') + cy.openProject('webpack5_wds4-react', ['--config-file', 'cypress-webpack-no-support.config.ts']) + cy.startAppServer('component') + + cy.visitApp() + cy.contains('App.cy.jsx').click() + cy.get('.passed > .num').should('contain', 1) + }) +}) diff --git a/packages/app/cypress/e2e/runner/runner.ui.cy.ts b/packages/app/cypress/e2e/runner/runner.ui.cy.ts index f30959a798..f888bb877b 100644 --- a/packages/app/cypress/e2e/runner/runner.ui.cy.ts +++ b/packages/app/cypress/e2e/runner/runner.ui.cy.ts @@ -270,7 +270,7 @@ describe('src/cypress/runner', () => { failCount: 1, }) - cy.get('.command-number:contains(25)').should('be.visible') + cy.get('.command-number-column:contains(25)').should('be.visible') }) it('file with empty suites only displays no tests found', () => { diff --git a/packages/app/cypress/e2e/settings.cy.ts b/packages/app/cypress/e2e/settings.cy.ts index acc1d6bc10..911f58afe3 100644 --- a/packages/app/cypress/e2e/settings.cy.ts +++ b/packages/app/cypress/e2e/settings.cy.ts @@ -77,6 +77,32 @@ describe('App: Settings', () => { expect((ctx.actions.electron.openExternal as SinonStub).lastCall.lastArg).to.eq('http:/test.cloud/cloud-project/settings') }) }) + + it('shows deferred remote cloud data after navigating from a run', { retries: 0 }, () => { + cy.remoteGraphQLIntercept(async (obj) => { + // Simulate a timeout so we don't resolve immediately, previously visiting the test runner + // and then leaving would cause this to fail, because it removed the event listeners + // for graphql-refetch. By namespacing the socket layer, we avoid the events of the + // runner from impacting the cloud behavior + await new Promise((resolve) => setTimeout(resolve, 500)) + + return obj.result + }) + + cy.startAppServer('e2e') + cy.loginUser() + cy.visitApp() + cy.get('.spec-list-container').scrollTo('bottom') + // Visit the test to trigger the ws.off() for the TR websockets + cy.contains('test1.js').click() + // Wait for the test to pass, so the test is completed + cy.get('.passed > .num').should('contain', 1) + cy.get(`[href='#/settings']`).click() + cy.contains('Dashboard Settings').click() + // Assert the data is not there before it arrives + cy.contains('Record Key').should('not.exist') + cy.contains('Record Key') + }) }) describe('Project Settings', () => { diff --git a/packages/app/cypress/e2e/sidebar_navigation.cy.ts b/packages/app/cypress/e2e/sidebar_navigation.cy.ts index af97ffe3e4..d6484152be 100644 --- a/packages/app/cypress/e2e/sidebar_navigation.cy.ts +++ b/packages/app/cypress/e2e/sidebar_navigation.cy.ts @@ -28,6 +28,7 @@ describe('Sidebar Navigation', () => { cy.openProject('todos') cy.startAppServer() cy.visitApp() + cy.contains('todos') }) it('expands the left nav bar by default', () => { @@ -50,7 +51,6 @@ describe('Sidebar Navigation', () => { it('closes the left nav bar when clicking the expand button (if expanded)', () => { cy.findByLabelText('Sidebar').closest('[aria-expanded]').should('have.attr', 'aria-expanded', 'true') - cy.contains('todos') cy.findAllByText('todos').eq(1).as('title') cy.get('@title').should('be.visible') @@ -65,7 +65,6 @@ describe('Sidebar Navigation', () => { it('closes the left nav bar when clicking the expand button and persist the state if browser is refreshed', () => { cy.findByLabelText('Sidebar').closest('[aria-expanded]').should('have.attr', 'aria-expanded', 'true') - cy.contains('todos') cy.findAllByText('todos').eq(1).as('title') cy.get('@title').should('be.visible') @@ -258,13 +257,13 @@ describe('Sidebar Navigation', () => { o.sinon.stub(ctx.actions.localSettings, 'setPreferences').resolves() }) - cy.get('[data-cy="reporter-panel"]').invoke('outerWidth').then(($initialWidth) => { - cy.get('[data-cy="panel2ResizeHandle"]').trigger('mousedown', { eventConstructor: 'MouseEvent' }) - .trigger('mousemove', { clientX: 400 }) - .trigger('mouseup', { eventConstructor: 'MouseEvent' }) - }) + cy.get('[data-cy="reporter-panel"]').invoke('outerWidth').should('eq', 450) - cy.withCtx((ctx, o) => { + cy.get('[data-cy="panel2ResizeHandle"]').trigger('mousedown', { eventConstructor: 'MouseEvent' }) + .trigger('mousemove', { clientX: 400 }) + .trigger('mouseup', { eventConstructor: 'MouseEvent' }) + + cy.withRetryableCtx((ctx, o) => { expect((ctx.actions.localSettings.setPreferences as SinonStub).lastCall.lastArg).to.eq('{"reporterWidth":336}') }) }) diff --git a/packages/app/cypress/e2e/specs.cy.ts b/packages/app/cypress/e2e/specs.cy.ts index 17ab460b4b..828215dca0 100644 --- a/packages/app/cypress/e2e/specs.cy.ts +++ b/packages/app/cypress/e2e/specs.cy.ts @@ -6,8 +6,8 @@ describe('App: Specs', () => { describe('Testing Type: E2E', () => { context('js project with default spec pattern', () => { beforeEach(() => { - cy.scaffoldProject('no-specs-no-storybook') - cy.openProject('no-specs-no-storybook') + cy.scaffoldProject('no-specs') + cy.openProject('no-specs') cy.startAppServer('e2e') cy.visitApp() @@ -193,14 +193,14 @@ describe('App: Specs', () => { context('ts project with default spec pattern', () => { beforeEach(() => { - cy.scaffoldProject('no-specs-no-storybook') - cy.openProject('no-specs-no-storybook') + cy.scaffoldProject('no-specs') + cy.openProject('no-specs') cy.withCtx(async (ctx) => { await ctx.actions.file.writeFileInProject('tsconfig.json', '{}') }) - cy.openProject('no-specs-no-storybook') + cy.openProject('no-specs') cy.startAppServer('e2e') cy.visitApp() @@ -469,10 +469,10 @@ describe('App: Specs', () => { viewportHeight: 768, viewportWidth: 1024, }, () => { - context('project without storybook', () => { + context('project with default spec pattern', () => { beforeEach(() => { - cy.scaffoldProject('no-specs-no-storybook') - cy.openProject('no-specs-no-storybook') + cy.scaffoldProject('no-specs') + cy.openProject('no-specs') cy.startAppServer('component') cy.visitApp() diff --git a/packages/app/cypress/e2e/subscriptions/configChange-subscription.cy.ts b/packages/app/cypress/e2e/subscriptions/configChange-subscription.cy.ts new file mode 100644 index 0000000000..a5f63cb61c --- /dev/null +++ b/packages/app/cypress/e2e/subscriptions/configChange-subscription.cy.ts @@ -0,0 +1,53 @@ +function updateProjectIdInCypressConfig (value: string) { + return cy.withCtx((ctx, o) => { + let config = ctx.actions.file.readFileInProject('cypress.config.js') + + config = config.replace(`projectId: 'abc123'`, `projectId: '${o.value}'`) + ctx.actions.file.writeFileInProject('cypress.config.js', config) + }, { value }) +} + +function updateViewportHeightInCypressConfig (value: number) { + return cy.withCtx((ctx, o) => { + let config = ctx.actions.file.readFileInProject('cypress.config.js') + + config = config.replace(`e2e: {`, `e2e: {\n viewportHeight: ${o.value},\n`) + ctx.actions.file.writeFileInProject('cypress.config.js', config) + }, { value }) +} + +describe('specChange subscription', () => { + beforeEach(() => { + cy.scaffoldProject('cypress-in-cypress') + cy.openProject('cypress-in-cypress') + cy.startAppServer() + cy.visitApp() + }) + + describe('on config page', () => { + it('responds to configChange event when viewport is changed', () => { + cy.get('a').contains('Settings').click() + cy.get('[data-cy="collapsible-header"]').contains('Project Settings').click() + cy.contains(`projectId: 'abc123'`) + updateProjectIdInCypressConfig('foo456') + cy.contains(`projectId: 'foo456'`) + }) + }) + + describe('on runner page', () => { + it('responds to configChange event and re-runs spec', () => { + // run spec + cy.contains('dom-content.spec').click() + + // wait until it has passed + cy.get('[data-model-state="passed"]').should('contain', 'renders the test content') + cy.get('button').contains('1000x660') + + // update the config - the spec should re-execute with the new viewportHeight + updateViewportHeightInCypressConfig(777) + + cy.get('[data-model-state="passed"]').should('contain', 'renders the test content') + cy.get('button').contains('1000x777') + }) + }) +}) diff --git a/packages/app/cypress/e2e/subscriptions/specChange-subscription.cy.ts b/packages/app/cypress/e2e/subscriptions/specChange-subscription.cy.ts index 83265af880..6930e11763 100644 --- a/packages/app/cypress/e2e/subscriptions/specChange-subscription.cy.ts +++ b/packages/app/cypress/e2e/subscriptions/specChange-subscription.cy.ts @@ -22,8 +22,8 @@ describe('specChange subscription', () => { .should('contain', 'dom-content.spec.js') .should('contain', 'dom-list.spec.js') - cy.withCtx(async (ctx, o) => { - await ctx.actions.file.writeFileInProject(o.path, '') + cy.withCtx((ctx, o) => { + ctx.actions.file.writeFileInProject(o.path, '') }, { path: getPathForPlatform('cypress/e2e/new-file.spec.js') }) cy.get('[data-cy="spec-item-link"]') diff --git a/packages/app/src/pages/Settings.vue b/packages/app/src/pages/Settings.vue index 936fae4e4a..63b80f2cd1 100644 --- a/packages/app/src/pages/Settings.vue +++ b/packages/app/src/pages/Settings.vue @@ -6,15 +6,25 @@ diff --git a/packages/app/src/pages/Specs/Runner.vue b/packages/app/src/pages/Specs/Runner.vue index 8895db5747..e8c6a4778f 100644 --- a/packages/app/src/pages/Specs/Runner.vue +++ b/packages/app/src/pages/Specs/Runner.vue @@ -25,10 +25,12 @@