From b5d3dae337342f28762e67be01a6f4eb886e32b6 Mon Sep 17 00:00:00 2001 From: Emily Rohrbough Date: Wed, 26 Jan 2022 11:27:41 -0600 Subject: [PATCH] test(launchpad): add e2e tests for Project Setup - Round 3 (#19704) Co-authored-by: Tyler Biethman Co-authored-by: Tyler Biethman --- packages/app/package.json | 2 +- .../data-context/src/actions/WizardActions.ts | 4 +- .../src/sources/WizardDataSource.ts | 6 +- .../cypress/e2e/support/e2eProjectDirs.ts | 1 + .../support/mock-graphql/stubgql-Wizard.ts | 6 +- .../schemaTypes/objectTypes/gql-Mutation.ts | 10 +- .../cypress/component/support/index.ts | 2 + .../cypress/e2e/onboarding-flow.cy.ts | 145 -------- .../launchpad/cypress/e2e/project-setup.cy.ts | 315 ++++++++++++++++-- .../cypress/e2e/support/e2eSupport.ts | 1 + packages/launchpad/package.json | 1 + .../launchpad/src/images/logos/nextjs.svg | 3 +- packages/launchpad/src/images/logos/nuxt.svg | 2 +- packages/launchpad/src/images/logos/react.svg | 2 +- packages/launchpad/src/images/logos/vite.svg | 2 +- packages/launchpad/src/images/logos/vue.svg | 2 +- .../launchpad/src/images/logos/webpack.svg | 2 +- .../src/setup/EnvironmentSetup.cy.tsx | 46 ++- .../launchpad/src/setup/EnvironmentSetup.vue | 68 ++-- .../src/setup/InstallDependencies.cy.tsx | 24 +- .../src/setup/InstallDependencies.vue | 8 +- .../launchpad/src/setup/ManualInstall.cy.tsx | 41 ++- .../launchpad/src/setup/ScaffoldedFiles.vue | 2 - .../launchpad/src/setup/SelectLanguage.vue | 2 +- packages/launchpad/src/setup/Wizard.vue | 48 +-- packages/launchpad/tsconfig.json | 1 + packages/reporter/package.json | 2 +- packages/runner-ct/package.json | 2 +- .../.storybook/main.js | 5 + .../app.js | 0 .../cypress.config.js | 3 + .../cypress/support/e2e.js | 0 yarn.lock | 5 + 33 files changed, 477 insertions(+), 286 deletions(-) delete mode 100644 packages/launchpad/cypress/e2e/onboarding-flow.cy.ts create mode 100644 system-tests/projects/pristine-with-e2e-testing-and-storybook/.storybook/main.js create mode 100644 system-tests/projects/pristine-with-e2e-testing-and-storybook/app.js create mode 100644 system-tests/projects/pristine-with-e2e-testing-and-storybook/cypress.config.js create mode 100644 system-tests/projects/pristine-with-e2e-testing-and-storybook/cypress/support/e2e.js diff --git a/packages/app/package.json b/packages/app/package.json index 79e63ad236..112b3bab14 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -41,7 +41,7 @@ "combine-properties": "0.1.0", "concurrently": "^6.2.0", "cross-env": "6.0.3", - "cypress-real-events": "1.4.0", + "cypress-real-events": "1.6.0", "faker": "5.5.3", "fuzzysort": "^1.1.4", "graphql": "^15.5.1", diff --git a/packages/data-context/src/actions/WizardActions.ts b/packages/data-context/src/actions/WizardActions.ts index c57afded55..4bf93a04f4 100644 --- a/packages/data-context/src/actions/WizardActions.ts +++ b/packages/data-context/src/actions/WizardActions.ts @@ -30,10 +30,10 @@ export class WizardActions { this.ctx.coreData.wizard.chosenFramework = framework if (framework !== 'react' && framework !== 'vue') { - this.setBundler('webpack') + return this.setBundler('webpack') } - return this.data + return this.setBundler(null) } setBundler (bundler: NexusGenEnums['SupportedBundlers'] | null) { diff --git a/packages/data-context/src/sources/WizardDataSource.ts b/packages/data-context/src/sources/WizardDataSource.ts index 59babc835e..6252fd271b 100644 --- a/packages/data-context/src/sources/WizardDataSource.ts +++ b/packages/data-context/src/sources/WizardDataSource.ts @@ -38,14 +38,14 @@ export class WizardDataSource { } get chosenFramework () { - return FRONTEND_FRAMEWORKS.find((f) => f.type === this.ctx.wizardData.chosenFramework) + return FRONTEND_FRAMEWORKS.find((f) => f.type === this.ctx.wizardData.chosenFramework) || null } get chosenBundler () { - return BUNDLERS.find((f) => f.type === this.ctx.wizardData.chosenBundler) + return BUNDLERS.find((f) => f.type === this.ctx.wizardData.chosenBundler) || null } get chosenLanguage () { - return CODE_LANGUAGES.find((f) => f.type === this.ctx.wizardData.chosenLanguage) + return CODE_LANGUAGES.find((f) => f.type === this.ctx.wizardData.chosenLanguage) || null } } diff --git a/packages/frontend-shared/cypress/e2e/support/e2eProjectDirs.ts b/packages/frontend-shared/cypress/e2e/support/e2eProjectDirs.ts index c07cf8452a..66dac9ad06 100644 --- a/packages/frontend-shared/cypress/e2e/support/e2eProjectDirs.ts +++ b/packages/frontend-shared/cypress/e2e/support/e2eProjectDirs.ts @@ -66,6 +66,7 @@ export const e2eProjectDirs = [ 'pristine', 'pristine-with-ct-testing', 'pristine-with-e2e-testing', + 'pristine-with-e2e-testing-and-storybook', 'react-code-gen', 'read-only-project-root', 'record', diff --git a/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Wizard.ts b/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Wizard.ts index c65995fb38..4a92a7e30d 100644 --- a/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Wizard.ts +++ b/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Wizard.ts @@ -1,5 +1,5 @@ import type { CodegenTypeMap, Wizard } from '../generated/test-graphql-types.gen' -import { BUNDLERS, CODE_LANGUAGES, FRONTEND_FRAMEWORKS } from '@packages/types/src/constants' +import { BUNDLERS, CODE_LANGUAGES, FRONTEND_FRAMEWORKS, PACKAGES_DESCRIPTIONS } from '@packages/types/src/constants' import { MaybeResolver, testNodeId } from './clientTestUtils' export const allBundlers = BUNDLERS.map((bundler, idx) => { @@ -15,13 +15,13 @@ export const stubWizard: MaybeResolver = { packagesToInstall: [ { ...testNodeId('WizardNpmPackage'), - description: 'Used to interact with React components via Cypress', + description: PACKAGES_DESCRIPTIONS['@cypress/react'], name: '@cypress/react', package: '@cypress/react', }, { ...testNodeId('WizardNpmPackage'), - description: 'Used to bundle code', + description: PACKAGES_DESCRIPTIONS['@cypress/webpack-dev-server'], name: '@cypress/webpack-dev-server', package: '@cypress/webpack-dev-server', }, diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts index ba329ead25..d5ba6d34cf 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts @@ -153,18 +153,20 @@ export const mutation = mutationType({ input: nonNull(arg({ type: WizardUpdateInput })), }, resolve: async (source, args, ctx) => { - if (args.input.bundler !== undefined) { - ctx.actions.wizard.setBundler(args.input.bundler) + if (args.input.framework) { + ctx.actions.wizard.setFramework(args.input.framework) } - if (args.input.framework !== undefined) { - ctx.actions.wizard.setFramework(args.input.framework) + if (args.input.bundler) { + ctx.actions.wizard.setBundler(args.input.bundler) } if (args.input.codeLanguage) { ctx.actions.wizard.setCodeLanguage(args.input.codeLanguage) } + // TODO: remove when live-mutations are implements + // signal to launchpad to reload the data context ctx.emitter.toLaunchpad() return ctx.wizardData diff --git a/packages/launchpad/cypress/component/support/index.ts b/packages/launchpad/cypress/component/support/index.ts index 845585bdee..9bc72c2228 100644 --- a/packages/launchpad/cypress/component/support/index.ts +++ b/packages/launchpad/cypress/component/support/index.ts @@ -20,6 +20,8 @@ import '../../../src/main.scss' import '@iconify/iconify' import '@purge-icons/generated' +import 'cypress-real-events/support' + import './commands' import './attachFileWithPath' import installCustomPercyCommand from '@packages/ui-components/cypress/support/customPercyCommand' diff --git a/packages/launchpad/cypress/e2e/onboarding-flow.cy.ts b/packages/launchpad/cypress/e2e/onboarding-flow.cy.ts deleted file mode 100644 index 540aa932f5..0000000000 --- a/packages/launchpad/cypress/e2e/onboarding-flow.cy.ts +++ /dev/null @@ -1,145 +0,0 @@ -describe('Launchpad: Onboarding Flow', () => { - beforeEach(() => { - cy.scaffoldProject('pristine') - cy.openProject('pristine') - }) - - it('can setup component testing', () => { - cy.visitLaunchpad() - cy.percySnapshot() - cy.get('[data-cy-testingType=component]').click() - cy.get('[data-testid=select-framework]').should('be.visible') - cy.percySnapshot() - cy.get('[data-testid=select-framework]').click() - cy.findByText('Vue.js').click() - cy.get('[data-testid=select-framework]').should('contain', 'Vue.js') - cy.get('[data-testid=select-bundler]') - .findByText(cy.i18n.setupPage.projectSetup.bundlerPlaceholder) - .click() - - cy.findByText('Webpack').click() - cy.get('[data-testid=select-bundler]').should('contain', 'Webpack') - - cy.percySnapshot() - - cy.reload() - cy.get('[data-testid=select-framework]').should('contain', 'Vue.js') - cy.get('[data-testid=select-bundler]').should('contain', 'Webpack') - cy.findByText('Next Step').click() - cy.get('h1').should('contain', 'Dependencies') - cy.findByText('I\'ve installed them').click() - cy.findByText('We added the following files to your project.') - cy.percySnapshot() - cy.findByText('Continue').click() - cy.withCtx((ctx) => { - return ctx.file.readFileInProject('cypress.config.js') - }) - - cy.findByText('Choose a Browser', { timeout: 10000 }) - cy.findByText('Choose your preferred browser for component testing.') - }) - - it('can setup component testing with TS', () => { - cy.visitLaunchpad() - cy.get('[data-cy-testingType=component]').click() - cy.get('[data-testid=select-framework]').click() - cy.findByText('React.js').click() - cy.get('[data-testid=select-framework]').should('contain', 'React.js') - cy.get('[data-testid=select-bundler]') - .findByText(cy.i18n.setupPage.projectSetup.bundlerPlaceholder) - .click() - - cy.findByText('Webpack').click() - cy.get('[data-testid=select-bundler]').should('contain', 'Webpack') - cy.reload() - cy.get('[data-testid=select-framework]').should('contain', 'React.js') - cy.get('[data-testid=select-bundler]').should('contain', 'Webpack') - cy.findByText('TypeScript').click() - cy.findByText('Next Step').click() - cy.get('h1').should('contain', 'Dependencies') - cy.findByText('I\'ve installed them').click() - cy.findByText('We added the following files to your project.') - cy.findByText('Continue').click() - cy.withCtx((ctx) => { - return ctx.file.readFileInProject('cypress.config.ts') - }) - }) - - it('can setup e2e testing', () => { - cy.visitLaunchpad() - cy.get('[data-cy-testingType=e2e]').click() - cy.findByText('We added the following files to your project.') - cy.percySnapshot() - cy.findByText('Continue').click() - cy.findByText('Choose a Browser') - cy.findByText('Choose your preferred browser for E2E testing.') - }) - - it('can setup e2e testing after component has been setup', () => { - cy.visitLaunchpad() - cy.get('[data-cy-testingType=component]').click() - cy.get('[data-testid=select-framework]').click() - cy.findByText('Vue.js').click() - cy.get('[data-testid=select-framework]').should('contain', 'Vue.js') - cy.get('[data-testid=select-bundler]') - .findByText(cy.i18n.setupPage.projectSetup.bundlerPlaceholder) - .click() - - cy.findByText('Webpack').click() - cy.get('[data-testid=select-bundler]').should('contain', 'Webpack') - cy.reload() - cy.get('[data-testid=select-framework]').should('contain', 'Vue.js') - cy.get('[data-testid=select-bundler]').should('contain', 'Webpack') - cy.findByText('Next Step').click() - cy.get('h1').should('contain', 'Dependencies') - cy.findByText('I\'ve installed them').click() - cy.findByText('We added the following files to your project.') - cy.findByText('Continue').click() - cy.findByText('Choose a Browser', { timeout: 10000 }) - - cy.findByText('Switch testing type').click() - - cy.visitLaunchpad() - cy.get('[data-cy-testingType=e2e]').click() - cy.findByText('We added the following files to your project.') - cy.get('[data-cy="changes"]') - cy.findByText('Continue').closest('button').should('be.disabled') - - cy.withCtx((ctx, o) => { - const tmpl = ` - const { defineConfig } = require("cypress") - module.exports = defineConfig({ - component: { - supportFile: 'cypress/support/component.js', - devServer: require('@cypress/webpack-dev-server'), - devServerConfig: {} - }, - e2e: { - supportFile: 'cypress/support/e2e.js', - specPattern: 'cypress/e2e/**/*.cy.{js,ts}', - viewportHeight: 660, - viewportWidth: 1000, - setupNodeEvents(on, config) { - // - }, - } - }) - ` - - ctx.actions.file.writeFileInProject('cypress.config.js', tmpl) - }) - - cy.findByText('Continue').closest('button').should('not.be.disabled').click() - cy.findByText('Choose a Browser', { timeout: 10000 }) - }) - - it('can open unconfigured component testing type, go back to the testing chooser', () => { - cy.scaffoldProject('pristine-with-e2e-testing') - cy.openProject('pristine-with-e2e-testing') - cy.visitLaunchpad() - cy.get('[data-cy-testingType=component]').click() - cy.get('h1').should('not.contain', 'Welcome to Cypress!') - cy.contains('Back').click() - cy.get('h1').should('contain', 'Welcome to Cypress!') - }) -}) diff --git a/packages/launchpad/cypress/e2e/project-setup.cy.ts b/packages/launchpad/cypress/e2e/project-setup.cy.ts index 41c7c5041a..cdff25f0d8 100644 --- a/packages/launchpad/cypress/e2e/project-setup.cy.ts +++ b/packages/launchpad/cypress/e2e/project-setup.cy.ts @@ -1,8 +1,11 @@ +import { FRONTEND_FRAMEWORKS, BUNDLERS, CODE_LANGUAGES, PACKAGES_DESCRIPTIONS } from '@packages/types/src/constants' + describe('Launchpad: Setup Project', () => { beforeEach(() => { cy.scaffoldProject('pristine') // not configured cy.scaffoldProject('pristine-with-ct-testing') // component configured cy.scaffoldProject('pristine-with-e2e-testing') // e2e configured + cy.scaffoldProject('pristine-with-e2e-testing-and-storybook') // e2e configured }) const verifyWelcomePage = ({ e2eIsConfigured, ctIsConfigured }) => { @@ -49,17 +52,27 @@ describe('Launchpad: Setup Project', () => { it('close modal with escape key', () => { cy.contains('Review the differences').click() cy.get('#app').should('have.attr', 'aria-hidden', 'true') - cy.findByRole('dialog', { name: 'Key Differences' }).should('be.visible') + + cy.findByRole('dialog', { name: 'Key Differences' }) + .as('aboutTestingTypes') + .should('be.visible') + cy.get('body').type('{esc}') cy.get('#app').should('not.have.attr', 'aria-hidden') + cy.get('@aboutTestingTypes').should('not.exist') }) it('closes modal by clicking outside of modal', () => { cy.contains('Review the differences').click() cy.get('#app').should('have.attr', 'aria-hidden', 'true') - cy.findByRole('dialog', { name: 'Key Differences' }).should('be.visible') + + cy.findByRole('dialog', { name: 'Key Differences' }) + .as('aboutTestingTypes') + .should('be.visible') + cy.get('body').click(5, 5) cy.get('#app').should('not.have.attr', 'aria-hidden') + cy.get('@aboutTestingTypes').should('not.exist') }) it('closes modal by clicking close button', () => { @@ -67,18 +80,21 @@ describe('Launchpad: Setup Project', () => { cy.get('#app').should('have.attr', 'aria-hidden', 'true') cy.findByRole('dialog', { name: 'Key Differences' }) + .as('aboutTestingTypes') .should('be.visible') .within(() => { cy.get('h2').contains('Key Differences').should('be.visible') }) cy.findByRole('button', { name: 'Close' }).click() - // cy.get('[aria-label=Close]').click() cy.get('#app').should('not.have.attr', 'aria-hidden') + cy.get('@aboutTestingTypes').should('not.exist') }) - // FIXME: enter key down isn't trigger close callback. working correctly when manually tested. - // could be related to this bug? https://github.com/cypress-io/cypress/issues/14864 + // Cypress enter key down isn't trigger close callback. Working correctly when manually tested + // or when using the cypress-real-evens plugin. + // Could be related to this bug? https://github.com/cypress-io/cypress/issues/14864 + // FIXME: https://github.com/cypress-io/cypress/pull/19726 it.skip('closes modal by pressing enter key when close button is focused', () => { cy.contains('Review the differences').click() cy.get('#app').should('have.attr', 'aria-hidden', 'true') @@ -98,7 +114,7 @@ describe('Launchpad: Setup Project', () => { }) cy.get('#app').should('not.have.attr', 'aria-hidden') - cy.get('@aboutTestTypes').should('not.be.visible') + cy.get('@aboutTestingTypes').should('not.exist') }) it('clicking "Need Help?" links to Cypress documentation', () => { @@ -140,7 +156,7 @@ describe('Launchpad: Setup Project', () => { // project has a cypress.configuration file with component testing configured describe('project that has not been configured for e2e', () => { // FIXME: ProjectLifecycleManager is skipping straight to browser pages when it should show setup page. - it.skip('shows the first step in configuration when selecting e2e tests', () => { + it.skip('shows the configuration setup page when selecting e2e tests', () => { cy.openProject('pristine-with-ct-testing') cy.visitLaunchpad() @@ -183,7 +199,7 @@ describe('Launchpad: Setup Project', () => { }) }) - it('shows the first step in configuration when opened via cli with --e2e flag', () => { + it('shows the configuration setup page when opened via cli with --e2e flag', () => { cy.openProject('pristine-with-ct-testing', ['--e2e']) cy.visitLaunchpad() @@ -201,33 +217,58 @@ describe('Launchpad: Setup Project', () => { }) }) - it('can setup e2e testing for a project not been configured for cypress', () => { - cy.openProject('pristine') - cy.visitLaunchpad() + describe('project not been configured for cypress', () => { + it('can setup e2e testing for a project', () => { + cy.openProject('pristine') + cy.visitLaunchpad() - verifyWelcomePage({ e2eIsConfigured: false, ctIsConfigured: false }) + verifyWelcomePage({ e2eIsConfigured: false, ctIsConfigured: false }) - // @ts-ignore - cy.get('body').tab().tab() + // @ts-ignore + cy.get('body').tab().tab() - cy.get('[data-cy-testingtype="e2e"]') - .should('have.focus') - .type('{enter}') + cy.get('[data-cy-testingtype="e2e"]') + .should('have.focus') + .type('{enter}') - cy.contains('h1', 'Configuration Files') - cy.findByText('We added the following files to your project.') + cy.contains('h1', 'Configuration Files') + cy.findByText('We added the following files to your project.') - cy.get('[data-cy=valid]').within(() => { - cy.contains('cypress.config.js') - cy.contains('cypress/support/e2e.js') - cy.contains('cypress/fixtures/example.json') + cy.get('[data-cy=valid]').within(() => { + cy.contains('cypress.config.js') + cy.contains('cypress/support/e2e.js') + cy.contains('cypress/fixtures/example.json') + }) + + cy.findByRole('button', { name: 'Continue' }) + .should('not.have.disabled') + .click() + + cy.contains(/(Initializing Config|Choose a Browser)/) + }) + + it('shows the configuration setup page when opened via cli with --e2e flag', () => { + cy.openProject('pristine-with-ct-testing', ['--e2e']) + cy.visitLaunchpad() + + cy.contains('h1', 'Configuration Files') + cy.contains('We added the following files to your project.') + + cy.get('[data-cy=changes]').within(() => { + cy.contains('cypress.config.js') + }) + + cy.get('[data-cy=valid]').within(() => { + cy.contains('cypress/support/e2e.js') + cy.contains('cypress/fixtures/example.json') + }) }) }) }) describe('Component setup', () => { describe('project has been configured for component testing', () => { - it('it skips the setup page when choosing component tests to run', () => { + it('skips the setup steps when choosing component tests to run', () => { cy.openProject('pristine-with-ct-testing') cy.visitLaunchpad() @@ -247,7 +288,7 @@ describe('Launchpad: Setup Project', () => { }) describe('project that has not been configured for component testing', () => { - it('shows the "choose framework" page when selecting component tests', () => { + it('shows the first setup page for configuration when selecting component tests', () => { cy.openProject('pristine-with-e2e-testing') cy.visitLaunchpad() @@ -257,6 +298,154 @@ describe('Launchpad: Setup Project', () => { cy.get('h1').should('contain', 'Project Setup') cy.contains('Confirm the front-end framework and bundler used in your project.') + + cy.findByRole('button', { + name: 'Front-end Framework Pick a framework', + expanded: false, + }) + .should('have.attr', 'aria-haspopup', 'true') + + cy.findByRole('button', { name: 'Next Step' }).should('have.disabled') + + cy.findByRole('button', { name: 'Back' }).click() + + verifyWelcomePage({ e2eIsConfigured: true, ctIsConfigured: false }) + }) + + const hasStorybookPermutations = [false, true] + + FRONTEND_FRAMEWORKS.forEach((framework) => { + hasStorybookPermutations.forEach((hasStorybookDep) => { + framework.supportedBundlers.forEach((testBundler) => { + const bundler = BUNDLERS.find((b) => b.type === testBundler) + + if (!bundler) { + throw new Error(`${framework.name} claims to support the bundler, ${testBundler}, however it is not a valid Cypress bundler.`) + } + + CODE_LANGUAGES.forEach((lang) => { + let testTitle = `can setup ${framework.name} + ${lang.name}` + + if (framework.supportedBundlers.length > 1) { + testTitle = `can setup ${framework.name} + ${bundler.name} + ${lang.name}` + } + + if (hasStorybookDep) { + testTitle += ` for project using Storybook` + } + + it(testTitle, () => { + cy.openProject(hasStorybookDep ? 'pristine-with-e2e-testing-and-storybook' : 'pristine-with-e2e-testing') + cy.visitLaunchpad() + + verifyWelcomePage({ e2eIsConfigured: true, ctIsConfigured: false }) + + cy.get('[data-cy-testingtype="component"]').click() + + cy.log('Choose project setup') + cy.get('h1').should('contain', 'Project Setup') + cy.contains('Confirm the front-end framework and bundler used in your project.') + + cy.findByRole('button', { name: 'Next Step' }) + .should('have.disabled') + .as('nextStepButton') + + cy.findByRole('button', { + name: 'Front-end Framework Pick a framework', + expanded: false, + }) + .click() + + cy.findByRole('option', { name: framework.name }).click() + cy.findByRole('button', { name: `Front-end Framework ${framework.name}` }) // ensure selected option updates + + if (framework.supportedBundlers.length > 1) { + cy.findByRole('button', { + name: 'Bundler Pick a bundler', + expanded: false, + }) + .should('have.attr', 'aria-haspopup', 'true') + .click() + .should('have.attr', 'aria-expanded', 'true') + + framework.supportedBundlers.forEach((supportedBundler) => { + cy.findByRole('option', { name: Cypress._.startCase(supportedBundler) }) + .find('svg') + .should('have.attr', 'data-cy', `${Cypress._.lowerCase(supportedBundler)}-logo`) + }) + + cy.findByRole('option', { name: bundler.name }) + .find('svg') + .should('have.attr', 'data-cy', `${Cypress._.lowerCase(bundler.name)}-logo`) + .click() + + cy.findByRole('button', { name: `Bundler ${bundler.name}` }) // ensure selected option updates + } + + cy.findByRole('button', { name: lang.name }).click() + + cy.log('Go to next step') + cy.get('@nextStepButton').should('not.have.disabled').click() + + cy.contains('h1', 'Install Dev Dependencies') + cy.contains('p', 'Paste the command below into your terminal to install the required packages.') + + cy.log('Return to previous step') + cy.findByRole('button', { name: 'Back' }) + .click() + + cy.findByRole('button', { name: `Front-end Framework ${framework.name}` }) + if (framework.supportedBundlers.length > 1) { + cy.findByRole('button', { name: `Bundler ${bundler.name}` }) + } + + cy.findByRole('button', { name: lang.name }) + cy.findByRole('button', { name: 'Next Step' }).click() + + cy.log('Go to next step and verify Install Dev Dependencies page') + cy.contains('h1', 'Install Dev Dependencies') + + let installCommand = `yarn add -D ${framework.package} ${bundler.package}` + + if (hasStorybookDep) { + installCommand += ` ${framework.storybookDep}` + } + + cy.contains('code', installCommand) + + const validatePackage = (packageName) => { + cy.validateExternalLink({ + name: packageName, + href: `https://www.npmjs.com/package/${packageName}`, + }) + + cy.contains(PACKAGES_DESCRIPTIONS[framework.package].split(' { + cy.contains('cypress.config.js') + }) + } + + cy.get('[data-cy=valid]').within(() => { + cy.contains('cypress/component/index.html') + cy.contains(`cypress/support/component.${lang.type}`) + cy.contains('cypress/fixtures/example.json') + }) + }) + }) + }) + }) }) it('opens to the "choose framework" page when opened via cli with --component flag', () => { @@ -269,20 +458,88 @@ describe('Launchpad: Setup Project', () => { }) describe('project not been configured for cypress', () => { - it('can setup component testing for a project not been configured for cypress', () => { + it('can setup component testing', () => { cy.openProject('pristine') cy.visitLaunchpad() verifyWelcomePage({ e2eIsConfigured: false, ctIsConfigured: false }) - // @ts-ignore - cy.get('body').tab().tab().tab() - cy.get('[data-cy-testingtype="component"]') + .focus() .should('have.focus') .type('{enter}') cy.findByText('Confirm the front-end framework and bundler used in your project.') + + cy.findByRole('button', { name: 'Front-end Framework Pick a framework' }).click() + cy.findByRole('option', { name: 'Create React App' }).click() + + cy.get('[data-testid="select-bundler"').should('not.exist') + cy.findByRole('button', { name: 'Next Step' }).should('not.have.disabled') + + cy.findByRole('button', { name: 'Back' }).click() + cy.get('[data-cy-testingtype="component"]').click() + + cy.findByRole('button', { name: 'Front-end Framework Create React App' }).click() + cy.findByRole('option', { name: 'React.js' }).click() + + cy.findByRole('button', { name: 'Next Step' }).should('have.disabled') + + cy.findByRole('button', { name: 'Bundler Pick a bundler' }).click() + cy.findByRole('option', { name: 'Webpack' }).click() + cy.findByRole('button', { name: 'Next Step' }).should('not.have.disabled') + + cy.findByRole('button', { name: 'Front-end Framework React.js' }).click() + cy.findByRole('option', { name: 'Create React App' }).click() + cy.findByRole('button', { name: 'Bundler Webpack' }).should('not.exist') + cy.findByRole('button', { name: 'Next Step' }).should('not.have.disabled') + + cy.findByRole('button', { name: 'Next Step' }).click() + cy.findByRole('button', { name: 'I\'ve installed them' }).click() + + cy.get('[data-cy=valid]').within(() => { + cy.contains('cypress.config.js') + cy.contains('cypress/component/index.html') + cy.contains(`cypress/support/component.js`) + cy.contains('cypress/fixtures/example.json') + }) + + // Fix me: https://cypress-io.atlassian.net/browse/UNIFY-981 + // cy.findByRole('button', { name: 'Continue' }).click() + // cy.contains(/(Initializing Config|Choose a Browser)/) + cy.findByRole('button', { name: 'Continue' }).click() + cy.contains(/(Initializing Config|Choose a Browser)/) + }) + + it('opens to the "choose framework" page when opened via cli with --component flag', () => { + cy.openProject('pristine') + cy.visitLaunchpad() + + verifyWelcomePage({ e2eIsConfigured: false, ctIsConfigured: false }) + + cy.get('[data-cy-testingtype="component"]') + .focus() + .should('have.focus') + .type('{enter}') + + cy.findByText('Confirm the front-end framework and bundler used in your project.') + + cy.findByRole('button', { name: 'Front-end Framework Pick a framework' }).click() + cy.findByRole('option', { name: 'Create React App' }).click() + cy.findByRole('button', { name: 'TypeScript' }).click() + cy.findByRole('button', { name: 'Next Step' }).click() + cy.findByRole('button', { name: 'I\'ve installed them' }).click() + + cy.get('[data-cy=valid]').within(() => { + cy.contains('cypress.config.ts') + cy.contains('cypress/component/index.html') + cy.contains(`cypress/support/component.ts`) + cy.contains('cypress/fixtures/example.json') + }) + + // Fix me: https://cypress-io.atlassian.net/browse/UNIFY-981 + // cy.findByRole('button', { name: 'Continue' }).click() + // cy.contains(/(Initializing Config|Choose a Browser)/) }) }) }) diff --git a/packages/launchpad/cypress/e2e/support/e2eSupport.ts b/packages/launchpad/cypress/e2e/support/e2eSupport.ts index c4e671b443..41660d0006 100644 --- a/packages/launchpad/cypress/e2e/support/e2eSupport.ts +++ b/packages/launchpad/cypress/e2e/support/e2eSupport.ts @@ -3,3 +3,4 @@ require('../../../../frontend-shared/cypress/e2e/support/e2eSupport') require('./dropFileWithPath') require('cypress-plugin-tab') +require('cypress-real-events/support') diff --git a/packages/launchpad/package.json b/packages/launchpad/package.json index 2b8e96f22f..c738cf73e5 100644 --- a/packages/launchpad/package.json +++ b/packages/launchpad/package.json @@ -43,6 +43,7 @@ "concurrently": "^6.2.0", "cross-env": "6.0.3", "cypress-plugin-tab": "1.0.5", + "cypress-real-events": "1.6.0", "graphql": "^15.5.1", "graphql-tag": "^2.12.5", "gravatar": "1.8.0", diff --git a/packages/launchpad/src/images/logos/nextjs.svg b/packages/launchpad/src/images/logos/nextjs.svg index 95fec6557a..2ff583328a 100644 --- a/packages/launchpad/src/images/logos/nextjs.svg +++ b/packages/launchpad/src/images/logos/nextjs.svg @@ -1,4 +1,3 @@ - + - diff --git a/packages/launchpad/src/images/logos/nuxt.svg b/packages/launchpad/src/images/logos/nuxt.svg index 4c6284afc6..0517b9b66a 100644 --- a/packages/launchpad/src/images/logos/nuxt.svg +++ b/packages/launchpad/src/images/logos/nuxt.svg @@ -1,4 +1,4 @@ - + diff --git a/packages/launchpad/src/images/logos/react.svg b/packages/launchpad/src/images/logos/react.svg index eaf5f348f7..292c3ab3ce 100644 --- a/packages/launchpad/src/images/logos/react.svg +++ b/packages/launchpad/src/images/logos/react.svg @@ -1,5 +1,5 @@ - + diff --git a/packages/launchpad/src/images/logos/vite.svg b/packages/launchpad/src/images/logos/vite.svg index fe3311b1af..31ef0a0785 100644 --- a/packages/launchpad/src/images/logos/vite.svg +++ b/packages/launchpad/src/images/logos/vite.svg @@ -1,4 +1,4 @@ - + diff --git a/packages/launchpad/src/images/logos/vue.svg b/packages/launchpad/src/images/logos/vue.svg index a1d285eb2a..ea24874dc6 100644 --- a/packages/launchpad/src/images/logos/vue.svg +++ b/packages/launchpad/src/images/logos/vue.svg @@ -1,2 +1,2 @@ - + diff --git a/packages/launchpad/src/images/logos/webpack.svg b/packages/launchpad/src/images/logos/webpack.svg index 13964053ca..4115d1049f 100644 --- a/packages/launchpad/src/images/logos/webpack.svg +++ b/packages/launchpad/src/images/logos/webpack.svg @@ -1 +1 @@ -icon +icon diff --git a/packages/launchpad/src/setup/EnvironmentSetup.cy.tsx b/packages/launchpad/src/setup/EnvironmentSetup.cy.tsx index b4eafd030f..1b39ba8189 100644 --- a/packages/launchpad/src/setup/EnvironmentSetup.cy.tsx +++ b/packages/launchpad/src/setup/EnvironmentSetup.cy.tsx @@ -1,27 +1,51 @@ import { EnvironmentSetupFragmentDoc } from '../generated/graphql-test' import EnvironmentSetup from './EnvironmentSetup.vue' -import type { WizardSetupData } from './Wizard.vue' +import { FRONTEND_FRAMEWORKS, CODE_LANGUAGES } from '../../../types/src/constants' describe('', () => { - it('playground', { viewportWidth: 800 }, () => { - const wizardSetupData: WizardSetupData = { - bundler: 'webpack', - framework: 'react', - codeLanguage: 'ts', - } - + it('default component', { viewportWidth: 800 }, () => { cy.mountFragment(EnvironmentSetupFragmentDoc, { render: (gqlVal) => (
), }) - cy.get('[data-testid="select-framework"]').click() - cy.contains('Nuxt.js').click() + cy.findByRole('button', { + name: 'Front-end Framework Pick a framework', + expanded: false, + }) + .should('have.attr', 'aria-haspopup', 'true') + .click() + .should('have.attr', 'aria-expanded', 'true') + + const frameworkIconName = (frameworkName) => { + if (frameworkName.includes('React')) { + return 'react-logo' + } + + if (frameworkName.includes('Vue')) { + return 'vue-logo' + } + + return `${Cypress._.lowerCase(frameworkName).replace(' ', '')}-logo` + } + + FRONTEND_FRAMEWORKS.forEach((framework) => { + cy.findByRole('option', { name: framework.name }) + .find('svg') + .should('have.attr', 'data-cy', frameworkIconName(framework.name)) + }) + + CODE_LANGUAGES.forEach((lang) => { + cy.findByRole('button', { name: lang.name }) + }) + + cy.findByRole('button', { name: 'Next Step' }) + .should('have.disabled') }) }) diff --git a/packages/launchpad/src/setup/EnvironmentSetup.vue b/packages/launchpad/src/setup/EnvironmentSetup.vue index 48ee8d755b..11ec3e4455 100644 --- a/packages/launchpad/src/setup/EnvironmentSetup.vue +++ b/packages/launchpad/src/setup/EnvironmentSetup.vue @@ -1,7 +1,7 @@ @@ -24,21 +22,11 @@ import EnvironmentSetup from './EnvironmentSetup.vue' import InstallDependencies from './InstallDependencies.vue' import { gql } from '@urql/core' -import { CodeLanguageEnum, WizardFragment, Wizard_WizardUpdateDocument } from '../generated/graphql' +import type { WizardFragment } from '../generated/graphql' import WarningList from '../warning/WarningList.vue' import { computed, ref } from 'vue' -import type { FrontendFramework, Bundler } from '@packages/types/src/constants' import LaunchpadHeader from './LaunchpadHeader.vue' import { useI18n } from '@cy/i18n' -import { useMutation } from '@urql/vue' - -export interface WizardSetupData { - bundler: Bundler['type'] | null - framework: FrontendFramework['type'] | null - codeLanguage: CodeLanguageEnum -} - -export type CurrentStep = 'selectFramework' | 'installDependencies' const props = defineProps<{ gql: WizardFragment @@ -46,37 +34,9 @@ const props = defineProps<{ const { t } = useI18n() -gql` -mutation Wizard_wizardUpdate($input: WizardUpdateInput!) { - wizardUpdate(input: $input) { - ...EnvironmentSetup - bundler { - id - type - } - framework { - id - type - } - } -} -` +export type CurrentStep = 'selectFramework' | 'installDependencies' const currentStep = ref('selectFramework') -const wizardSetupData = ref({ - bundler: props.gql.wizard.bundler?.type ?? null, - framework: props.gql.wizard.framework?.type ?? null, - codeLanguage: 'js', -}) - -const wizardUpdateMutation = useMutation(Wizard_WizardUpdateDocument) - -const onWizardSetup = (key: K, val: WizardSetupData[K]) => { - wizardSetupData.value[key] = val - wizardUpdateMutation.executeMutation({ - input: wizardSetupData.value, - }) -} const setCurrentStep = (step: CurrentStep) => { currentStep.value = step diff --git a/packages/launchpad/tsconfig.json b/packages/launchpad/tsconfig.json index 271347fe6f..2e4390dc7a 100644 --- a/packages/launchpad/tsconfig.json +++ b/packages/launchpad/tsconfig.json @@ -11,6 +11,7 @@ "../frontend-shared/cypress/**/*.ts" ], "compilerOptions": { + "types": ["cypress", "cypress-real-events", "@intlify/vite-plugin-vue-i18n/client"], "paths": { "@cy/i18n": ["../frontend-shared/src/locales/i18n"], "@cy/components/*": ["../frontend-shared/src/components/*"], diff --git a/packages/reporter/package.json b/packages/reporter/package.json index 923036ffbc..0bde6b1356 100644 --- a/packages/reporter/package.json +++ b/packages/reporter/package.json @@ -24,7 +24,7 @@ "classnames": "2.3.1", "css-element-queries": "1.2.3", "cypress-multi-reporters": "1.4.0", - "cypress-real-events": "1.4.0", + "cypress-real-events": "1.6.0", "lodash": "^4.17.21", "markdown-it": "11.0.0", "mobx": "5.15.4", diff --git a/packages/runner-ct/package.json b/packages/runner-ct/package.json index 04e1c61b9e..3f71ddd3dc 100644 --- a/packages/runner-ct/package.json +++ b/packages/runner-ct/package.json @@ -33,7 +33,7 @@ "chai": "^4.2.0", "classnames": "2.3.1", "clean-webpack-plugin": "^3.0.0", - "cypress-real-events": "1.4.0", + "cypress-real-events": "1.6.0", "eslint-plugin-mocha": "^8.0.0", "eslint-plugin-react": "^7.22.0", "eslint-plugin-react-hooks": "^4.2.0", diff --git a/system-tests/projects/pristine-with-e2e-testing-and-storybook/.storybook/main.js b/system-tests/projects/pristine-with-e2e-testing-and-storybook/.storybook/main.js new file mode 100644 index 0000000000..56b44b8694 --- /dev/null +++ b/system-tests/projects/pristine-with-e2e-testing-and-storybook/.storybook/main.js @@ -0,0 +1,5 @@ +module.exports = { + 'stories': [ + '../src/**/*.stories.@(js|jsx|ts|tsx)', + ], +} diff --git a/system-tests/projects/pristine-with-e2e-testing-and-storybook/app.js b/system-tests/projects/pristine-with-e2e-testing-and-storybook/app.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/system-tests/projects/pristine-with-e2e-testing-and-storybook/cypress.config.js b/system-tests/projects/pristine-with-e2e-testing-and-storybook/cypress.config.js new file mode 100644 index 0000000000..54497f5a24 --- /dev/null +++ b/system-tests/projects/pristine-with-e2e-testing-and-storybook/cypress.config.js @@ -0,0 +1,3 @@ +module.exports = { + e2e: {}, +} diff --git a/system-tests/projects/pristine-with-e2e-testing-and-storybook/cypress/support/e2e.js b/system-tests/projects/pristine-with-e2e-testing-and-storybook/cypress/support/e2e.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/yarn.lock b/yarn.lock index 1a09486c5a..1158bcba75 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17237,6 +17237,11 @@ cypress-real-events@1.4.0: resolved "https://registry.yarnpkg.com/cypress-real-events/-/cypress-real-events-1.4.0.tgz#39575031a4020581e0bbf105d7a306ee57d94f48" integrity sha512-1s4BQN1D++vFSuaad0qKsWcoApM5tQqPBFyDJEa6JeCZIsAdgMdGLuKi5QNIdl5KTJix0jxglzFJAThyz3borQ== +cypress-real-events@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/cypress-real-events/-/cypress-real-events-1.6.0.tgz#277024b62a324b6937760a700e831e795c021040" + integrity sha512-QxXm0JsQkCrb2uH+fMXNDQ5kNWTzX3OtndBafdsZmNV19j+6JuTK9n52B1YVxrDrr/qzPAojcHJc5PNoQvwp+w== + d3-array@^1.2.0: version "1.2.4" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f"