test(launchpad): add e2e tests for Project Setup - Round 3 (#19704)

Co-authored-by: Tyler Biethman <tbiethman@users.noreply.github.com>
Co-authored-by: Tyler Biethman <tbiethman@gmail.com>
This commit is contained in:
Emily Rohrbough
2022-01-26 11:27:41 -06:00
committed by GitHub
parent 5ff647e364
commit b5d3dae337
33 changed files with 477 additions and 286 deletions

View File

@@ -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",

View File

@@ -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) {

View File

@@ -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
}
}

View File

@@ -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',

View File

@@ -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<Wizard> = {
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',
},

View File

@@ -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

View File

@@ -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'

View File

@@ -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!')
})
})

View File

@@ -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('<span')[0])
}
validatePackage(framework.package)
validatePackage(bundler.package)
if (hasStorybookDep) {
validatePackage(framework.storybookDep)
}
cy.findByRole('button', { name: 'I\'ve installed them' }).click()
// FIXME: remove if-check once this is fixed. https://cypress-io.atlassian.net/browse/UNIFY-980
if (lang.type !== 'ts') {
cy.get('[data-cy=changes]').within(() => {
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)/)
})
})
})

View File

@@ -3,3 +3,4 @@ require('../../../../frontend-shared/cypress/e2e/support/e2eSupport')
require('./dropFileWithPath')
require('cypress-plugin-tab')
require('cypress-real-events/support')

View File

@@ -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",

View File

@@ -1,4 +1,3 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<svg data-cy="nextjs-logo" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path class="icon-dark" d="M386.3985596,35.5079727C217.0600281-64.0607605,1.8845311,57.5499001,0.012267,253.8817902c-1.8282685,191.716507,201.0625916,315.5454712,370.0206604,231.1632233L185.5603943,213.6362915l0.0000305,167.9969177c0,18.6138916-35.6191101,18.6138916-35.6191101,0V156.4207916c0-14.7758484,27.4472504-15.9884033,35.2252045-3.1443481l210.2631683,317.2959595C553.3806763,368.835144,551.2467041,132.4364166,386.3985596,35.5079727z M362.6429443,353.4465332l-35.7316284-54.5765381V149.4583282c0-13.9324646,35.7316284-13.9324646,35.7316284,0V353.4465332z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 667 B

After

Width:  |  Height:  |  Size: 688 B

View File

@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400">
<svg data-cy="nuxtjs-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400">
<g fill-rule="nonzero" transform="translate(0 50)" fill="none">
<path d="M227.92099 83.45116l-13.6889 24.10141-46.8148-82.44693L23.7037 278.17052h97.3037c0 13.31084 10.61252 24.10142 23.70371 24.10142H23.70371c-8.46771 0-16.29145-4.59601-20.5246-12.05272-4.23315-7.4567-4.23272-16.64312.00114-24.0994L146.89383 13.05492c4.23415-7.45738 12.0596-12.05138 20.5284-12.05138 8.46878 0 16.29423 4.594 20.52839 12.05138l39.97037 70.39623z" fill="#00C58E"/>
<path d="M331.6642 266.11981l-90.05432-158.56724-13.6889-24.10141-13.68888 24.10141-90.04445 158.56724c-4.23385 7.45629-4.23428 16.64271-.00113 24.09941 4.23314 7.4567 12.05689 12.05272 20.5246 12.05272h166.4c8.46946 0 16.29644-4.591 20.532-12.04837 4.23555-7.45736 4.23606-16.64592.00132-24.10376h.01976zM144.7111 278.17052L227.921 131.65399l83.19012 146.51653h-166.4z" fill="#2F495E"/>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<svg data-cy="react-logo" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<g id="group">
<path d="M495.3 248.5 C495.3 216 454.6 185.2 392.2 166.1 406.6 102.5 400.2 51.9 372 35.7 365.5 31.9 357.9 30.1 349.6 30.1 L349.6 52.4 C354.2 52.4 357.9 53.3 361 55 374.6 62.8 380.5 92.5 375.9 130.7 374.8 140.1 373 150 370.8 160.1 351.2 155.3 329.8 151.6 307.3 149.2 293.8 130.7 279.8 113.9 265.7 99.2 298.3 68.9 328.9 52.3 349.7 52.3 L349.7 30 C322.2 30 286.2 49.6 249.8 83.6 213.4 49.8 177.4 30.4 149.9 30.4 L149.9 52.7 C170.6 52.7 201.3 69.2 233.9 99.3 219.9 114 205.9 130.7 192.6 149.2 170 151.6 148.6 155.3 129 160.2 126.7 150.2 125 140.5 123.8 131.2 119.1 93 124.9 63.3 138.4 55.4 141.4 53.6 145.3 52.8 149.9 52.8 L149.9 30.5 C141.5 30.5 133.9 32.3 127.3 36.1 99.2 52.3 92.9 102.8 107.4 166.2 45.2 185.4 4.7 216.1 4.7 248.5 4.7 281 45.4 311.8 107.8 330.9 93.4 394.5 99.8 445.1 128 461.3 134.5 465.1 142.1 466.9 150.5 466.9 178 466.9 214 447.3 250.4 413.3 286.8 447.1 322.8 466.5 350.3 466.5 358.7 466.5 366.3 464.7 372.9 460.9 401 444.7 407.3 394.2 392.8 330.8 454.8 311.7 495.3 280.9 495.3 248.5 Z M365.1 181.8 C361.4 194.7 356.8 208 351.6 221.3 347.5 213.3 343.2 205.3 338.5 197.3 333.9 189.3 329 181.5 324.1 173.9 338.3 176 352 178.6 365.1 181.8 Z M319.3 288.3 C311.5 301.8 303.5 314.6 295.2 326.5 280.3 327.8 265.2 328.5 250 328.5 234.9 328.5 219.8 327.8 205 326.6 196.7 314.7 188.6 302 180.8 288.6 173.2 275.5 166.3 262.2 160 248.8 166.2 235.4 173.2 222 180.7 208.9 188.5 195.4 196.5 182.6 204.8 170.7 219.7 169.4 234.8 168.7 250 168.7 265.1 168.7 280.2 169.4 295 170.6 303.3 182.5 311.4 195.2 319.2 208.6 326.8 221.7 333.7 235 340 248.4 333.7 261.8 326.8 275.2 319.3 288.3 Z M351.6 275.3 C357 288.7 361.6 302.1 365.4 315.1 352.3 318.3 338.5 321 324.2 323.1 329.1 315.4 334 307.5 338.6 299.4 343.2 291.4 347.5 283.3 351.6 275.3 Z M250.2 382 C240.9 372.4 231.6 361.7 222.4 350 231.4 350.4 240.6 350.7 249.9 350.7 259.3 350.7 268.6 350.5 277.7 350 268.7 361.7 259.4 372.4 250.2 382 Z M175.8 323.1 C161.6 321 147.9 318.4 134.8 315.2 138.5 302.3 143.1 289 148.3 275.7 152.4 283.7 156.7 291.7 161.4 299.7 166.1 307.7 170.9 315.5 175.8 323.1 Z M249.7 115 C259 124.6 268.3 135.3 277.5 147 268.5 146.6 259.3 146.3 250 146.3 240.6 146.3 231.3 146.5 222.2 147 231.2 135.3 240.5 124.6 249.7 115 Z M175.7 173.9 C170.8 181.6 165.9 189.5 161.3 197.6 156.7 205.6 152.4 213.6 148.3 221.6 142.9 208.2 138.3 194.8 134.5 181.8 147.6 178.7 161.4 176 175.7 173.9 Z M85.2 299.1 C49.8 284 26.9 264.2 26.9 248.5 26.9 232.8 49.8 212.9 85.2 197.9 93.8 194.2 103.2 190.9 112.9 187.8 118.6 207.4 126.1 227.8 135.4 248.7 126.2 269.5 118.8 289.8 113.2 309.3 103.3 306.2 93.9 302.8 85.2 299.1 Z M139 442 C125.4 434.2 119.5 404.5 124.1 366.3 125.2 356.9 127 347 129.2 336.9 148.8 341.7 170.2 345.4 192.7 347.8 206.2 366.3 220.2 383.1 234.3 397.8 201.7 428.1 171.1 444.7 150.3 444.7 145.8 444.6 142 443.7 139 442 Z M376.2 365.8 C380.9 404 375.1 433.7 361.6 441.6 358.6 443.4 354.7 444.2 350.1 444.2 329.4 444.2 298.7 427.7 266.1 397.6 280.1 382.9 294.1 366.2 307.4 347.7 330 345.3 351.4 341.6 371 336.7 373.3 346.8 375.1 356.5 376.2 365.8 Z M414.7 299.1 C406.1 302.8 396.7 306.1 387 309.2 381.3 289.6 373.8 269.2 364.5 248.3 373.7 227.5 381.1 207.2 386.7 187.7 396.6 190.8 406 194.2 414.8 197.9 450.2 213 473.1 232.8 473.1 248.5 473 264.2 450.1 284.1 414.7 299.1 Z" fill="#3077c6" fill-opacity="1" stroke="none"/>
<path d="M295.6 248.5 C295.6 273.739 275.139 294.2 249.9 294.2 224.661 294.2 204.2 273.739 204.2 248.5 204.2 223.261 224.661 202.8 249.9 202.8 275.139 202.8 295.6 223.261 295.6 248.5 Z" fill="#3077c6" fill-opacity="1" stroke="none"/>

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -1,4 +1,4 @@
<svg viewBox="0 0 410 404" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg data-cy="vite-logo" viewBox="0 0 410 404" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M399.641 59.5246L215.643 388.545C211.844 395.338 202.084 395.378 198.228 388.618L10.5817 59.5563C6.38087 52.1896 12.6802 43.2665 21.0281 44.7586L205.223 77.6824C206.398 77.8924 207.601 77.8904 208.776 77.6763L389.119 44.8058C397.439 43.2894 403.768 52.1434 399.641 59.5246Z" fill="url(#paint0_linear)"/>
<path d="M292.965 1.5744L156.801 28.2552C154.563 28.6937 152.906 30.5903 152.771 32.8664L144.395 174.33C144.198 177.662 147.258 180.248 150.51 179.498L188.42 170.749C191.967 169.931 195.172 173.055 194.443 176.622L183.18 231.775C182.422 235.487 185.907 238.661 189.532 237.56L212.947 230.446C216.577 229.344 220.065 232.527 219.297 236.242L201.398 322.875C200.278 328.294 207.486 331.249 210.492 326.603L212.5 323.5L323.454 102.072C325.312 98.3645 322.108 94.137 318.036 94.9228L279.014 102.454C275.347 103.161 272.227 99.746 273.262 96.1583L298.731 7.86689C299.767 4.27314 296.636 0.855181 292.965 1.5744Z" fill="url(#paint1_linear)"/>
<defs>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,2 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" viewBox="0 0 261.76 226.69" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(1.3333 0 0 -1.3333 -76.311 313.34)"><g transform="translate(178.06 235.01)"><path d="m0 0-22.669-39.264-22.669 39.264h-75.491l98.16-170.02 98.16 170.02z" fill="#41b883"/></g><g transform="translate(178.06 235.01)"><path d="m0 0-22.669-39.264-22.669 39.264h-36.227l58.896-102.01 58.896 102.01z" fill="#34495e"/></g></g></svg>
<svg data-cy="vue-logo" version="1.1" viewBox="0 0 261.76 226.69" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(1.3333 0 0 -1.3333 -76.311 313.34)"><g transform="translate(178.06 235.01)"><path d="m0 0-22.669-39.264-22.669 39.264h-75.491l98.16-170.02 98.16 170.02z" fill="#41b883"/></g><g transform="translate(178.06 235.01)"><path d="m0 0-22.669-39.264-22.669 39.264h-36.227l58.896-102.01 58.896 102.01z" fill="#34495e"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 467 B

After

Width:  |  Height:  |  Size: 486 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 774 875.7"><title>icon</title><path fill="#FFF" d="M387 0l387 218.9v437.9L387 875.7 0 656.8V218.9z"/><path fill="#8ed6fb" d="M704.9 641.7L399.8 814.3V679.9l190.1-104.6 115 66.4zm20.9-18.9V261.9l-111.6 64.5v232l111.6 64.4zM67.9 641.7L373 814.3V679.9L182.8 575.3 67.9 641.7zM47 622.8V261.9l111.6 64.5v232L47 622.8zm13.1-384.3L373 61.5v129.9L172.5 301.7l-1.6.9-110.8-64.1zm652.6 0l-312.9-177v129.9l200.5 110.2 1.6.9 110.8-64z"/><path fill="#1c78c0" d="M373 649.3L185.4 546.1V341.8L373 450.1v199.2zm26.8 0l187.6-103.1V341.8L399.8 450.1v199.2zm-13.4-207zM198.1 318.2l188.3-103.5 188.3 103.5-188.3 108.7-188.3-108.7z"/></svg>
<svg data-cy="webpack-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 774 875.7"><title>icon</title><path fill="#FFF" d="M387 0l387 218.9v437.9L387 875.7 0 656.8V218.9z"/><path fill="#8ed6fb" d="M704.9 641.7L399.8 814.3V679.9l190.1-104.6 115 66.4zm20.9-18.9V261.9l-111.6 64.5v232l111.6 64.4zM67.9 641.7L373 814.3V679.9L182.8 575.3 67.9 641.7zM47 622.8V261.9l111.6 64.5v232L47 622.8zm13.1-384.3L373 61.5v129.9L172.5 301.7l-1.6.9-110.8-64.1zm652.6 0l-312.9-177v129.9l200.5 110.2 1.6.9 110.8-64z"/><path fill="#1c78c0" d="M373 649.3L185.4 546.1V341.8L373 450.1v199.2zm26.8 0l187.6-103.1V341.8L399.8 450.1v199.2zm-13.4-207zM198.1 318.2l188.3-103.5 188.3 103.5-188.3 108.7-188.3-108.7z"/></svg>

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 696 B

View File

@@ -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('<EnvironmentSetup />', () => {
it('playground', { viewportWidth: 800 }, () => {
const wizardSetupData: WizardSetupData = {
bundler: 'webpack',
framework: 'react',
codeLanguage: 'ts',
}
it('default component', { viewportWidth: 800 }, () => {
cy.mountFragment(EnvironmentSetupFragmentDoc, {
render: (gqlVal) => (
<div class="m-10">
<EnvironmentSetup
gql={gqlVal}
data={wizardSetupData}
nextFn={cy.stub()}
/>
</div>
),
})
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')
})
})

View File

@@ -1,7 +1,7 @@
<template>
<WizardLayout
:back-fn="onBack"
:next-fn="onNext"
:next-fn="props.nextFn"
:can-navigate-forward="canNavigateForward"
class="max-w-640px"
>
@@ -13,10 +13,10 @@
:label="t('setupPage.projectSetup.frameworkLabel')"
selector-type="framework"
data-testid="select-framework"
@select-framework="val => emit('wizardSetup', 'framework', val)"
@select-framework="val => onWizardSetup('framework', val)"
/>
<SelectFwOrBundler
v-if="props.gql.framework?.type && (!props.gql.bundler || bundlers.length > 1)"
v-if="props.gql.framework?.type && bundlers.length > 1"
class="pt-3px"
:options="bundlers || []"
:value="props.gql.bundler?.type ?? undefined"
@@ -24,13 +24,13 @@
:label="t('setupPage.projectSetup.bundlerLabel')"
selector-type="bundler"
data-testid="select-bundler"
@select-bundler="val => emit('wizardSetup', 'bundler', val)"
@select-bundler="val => onWizardSetup('bundler', val)"
/>
<SelectLanguage
:name="t('setupPage.projectSetup.languageLabel')"
:options="languages || []"
:value="props.gql.language?.id ?? 'js'"
@select="val => emit('wizardSetup', 'codeLanguage', val)"
:value="props.gql.language?.type ?? 'js'"
@select="val => onWizardSetup('codeLanguage', val)"
/>
</div>
</WizardLayout>
@@ -42,17 +42,16 @@ import WizardLayout from './WizardLayout.vue'
import SelectFwOrBundler from './SelectFwOrBundler.vue'
import SelectLanguage from './SelectLanguage.vue'
import { gql } from '@urql/core'
import { EnvironmentSetupFragment, EnvironmentSetup_ClearTestingTypeDocument } from '../generated/graphql'
import {
WizardUpdateInput,
EnvironmentSetupFragment,
EnvironmentSetup_ClearTestingTypeDocument,
EnvironmentSetup_WizardUpdateDocument,
} from '../generated/graphql'
import { useI18n } from '@cy/i18n'
import { sortBy } from 'lodash'
import type { CurrentStep, WizardSetupData } from './Wizard.vue'
import { useMutation } from '@urql/vue'
const emit = defineEmits<{
(event: 'navigate', toPage: CurrentStep): void
<K extends keyof WizardSetupData>(event: 'wizardSetup', key: K, val: WizardSetupData[K]): void
}>()
gql`
fragment EnvironmentSetup on Wizard {
bundler {
@@ -101,13 +100,13 @@ fragment EnvironmentSetup on Wizard {
const props = defineProps<{
gql: EnvironmentSetupFragment
data: WizardSetupData
nextFn: () => void,
}>()
const { t } = useI18n()
const bundlers = computed(() => {
const _bundlers = props.gql.framework?.supportedBundlers ?? props.gql.allBundlers
const _bundlers = props.gql.framework?.supportedBundlers || []
return _bundlers.map((b) => {
return {
@@ -120,8 +119,32 @@ const frameworks = computed(() => {
return sortBy((props.gql.frameworks ?? []).map((f) => ({ ...f })), 'category')
})
const onNext = () => {
emit('navigate', 'installDependencies')
gql`
mutation EnvironmentSetup_wizardUpdate($input: WizardUpdateInput!) {
wizardUpdate(input: $input) {
...EnvironmentSetup
bundler {
id
type
}
framework {
id
type
}
}
}
`
const wizardUpdateMutation = useMutation(EnvironmentSetup_WizardUpdateDocument)
const onWizardSetup = <K extends keyof WizardUpdateInput>(key: K, val: WizardUpdateInput[K]) => {
const input = {} as unknown as WizardUpdateInput
input[key] = val
wizardUpdateMutation.executeMutation({
input,
})
}
gql`
@@ -136,13 +159,18 @@ mutation EnvironmentSetup_ClearTestingType {
}
`
const mutation = useMutation(EnvironmentSetup_ClearTestingTypeDocument)
const clearTestingTypeMutation = useMutation(EnvironmentSetup_ClearTestingTypeDocument)
const onBack = () => {
mutation.executeMutation({})
clearTestingTypeMutation.executeMutation({})
}
const languages = computed(() => props.gql.allLanguages ?? [])
const canNavigateForward = computed(() => Object.values(props.data).filter((f) => f).length === 3)
const canNavigateForward = computed(() => {
const { bundler, framework, language } = props.gql
return bundler !== null && framework !== null && language !== null
})
</script>

View File

@@ -1,12 +1,15 @@
import { defaultMessages } from '@cy/i18n'
import InstallDependencies from './InstallDependencies.vue'
import { InstallDependenciesFragmentDoc } from '../generated/graphql-test'
import { defaultMessages } from '@cy/i18n'
import { PACKAGES_DESCRIPTIONS } from '../../../types/src/constants'
describe('<InstallDependencies />', () => {
beforeEach(() => {
beforeEach(function () {
this.onBack = cy.stub()
cy.mountFragment(InstallDependenciesFragmentDoc, {
render: (gqlVal) => {
return <InstallDependencies gql={gqlVal} />
return <InstallDependencies gql={gqlVal} backFn={this.onBack}/>
},
})
})
@@ -20,8 +23,8 @@ describe('<InstallDependencies />', () => {
.should('be.visible')
.and('have.attr', 'href', 'https://www.npmjs.com/package/@cypress/webpack-dev-server')
cy.contains('Used to interact with React components via Cypress').should('be.visible')
cy.contains('Used to bundle code').should('be.visible')
cy.contains(PACKAGES_DESCRIPTIONS['@cypress/react'].split('<span')[0])
cy.contains(PACKAGES_DESCRIPTIONS['@cypress/webpack-dev-server'].split('<span')[0])
cy.percySnapshot()
})
@@ -31,4 +34,15 @@ describe('<InstallDependencies />', () => {
cy.contains('button', defaultMessages.setupPage.install.confirmManualInstall).should('be.visible')
cy.contains('button', defaultMessages.setupPage.step.back).should('be.visible')
})
it('triggers back button callback', function () {
cy.findByRole('button', {
name: defaultMessages.setupPage.step.back,
})
.should('be.visible')
.click()
.then(() => {
expect(this.onBack).to.have.been.calledOnce
})
})
})

View File

@@ -2,7 +2,7 @@
<WizardLayout
:next=" t('setupPage.install.confirmManualInstall')"
:can-navigate-forward="true"
:back-fn="() => emits('navigate', 'selectFramework')"
:back-fn="props.backFn"
:next-fn="confirmInstalled"
class="max-w-640px"
>
@@ -17,7 +17,6 @@ import WizardLayout from './WizardLayout.vue'
import ManualInstall from './ManualInstall.vue'
import { gql } from '@urql/core'
import { InstallDependenciesFragment, InstallDependencies_ScaffoldFilesDocument } from '../generated/graphql'
import type { CurrentStep } from './Wizard.vue'
import { useI18n } from '@cy/i18n'
import { useMutation } from '@urql/vue'
@@ -29,10 +28,6 @@ mutation InstallDependencies_scaffoldFiles {
}
`
const emits = defineEmits<{
(event: 'navigate', currentStep: CurrentStep): void
}>()
gql`
fragment InstallDependencies on Query {
...ManualInstall
@@ -41,6 +36,7 @@ fragment InstallDependencies on Query {
const props = defineProps<{
gql: InstallDependenciesFragment
backFn: () => void,
}>()
const { t } = useI18n()

View File

@@ -1,8 +1,12 @@
import { ManualInstallFragmentDoc } from '../generated/graphql-test'
import ManualInstall from './ManualInstall.vue'
import { PACKAGES_DESCRIPTIONS } from '../../../types/src/constants'
describe('<ManualInstall />', () => {
it('playground', { viewportWidth: 800, viewportHeight: 600 }, () => {
it('lists packages and can copy install command to clipboard', { viewportWidth: 800, viewportHeight: 600 }, () => {
const framework = '@cypress/react'
const bundler = '@cypress/webpack-dev-server'
cy.mountFragment(ManualInstallFragmentDoc, {
render: (gqlVal) => (
<div class="rounded border-1 border-gray-400 m-10">
@@ -10,5 +14,40 @@ describe('<ManualInstall />', () => {
</div>
),
})
const installCommand = `yarn add -D ${framework} ${bundler}`
// @ts-ignore
cy.findByRole('button', { name: 'Copy' }).realClick()
cy.findByRole('button', { name: 'Copied!' })
if (Cypress.config('browser').name === 'chrome') {
cy.wrap(Cypress.automation('remote:debugger:protocol', {
command: 'Browser.grantPermissions',
params: {
permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'],
origin: window.location.origin,
},
}))
}
cy.window().its('navigator.permissions')
.invoke('query', { name: 'clipboard-read' })
.its('state').then(cy.log)
cy.window().its('navigator.clipboard')
.invoke('readText')
.should('equal', installCommand)
const validatePackage = (packageName) => {
cy.findByRole('link', { name: packageName })
.should('have.attr', 'href', `https://www.npmjs.com/package/${packageName}`)
cy.contains(PACKAGES_DESCRIPTIONS[framework].split('<span')[0])
}
validatePackage(framework)
validatePackage(bundler)
})
})

View File

@@ -32,12 +32,10 @@ import { useI18n } from '@cy/i18n'
import Button from '@cy/components/Button.vue'
import FileRow from '../components/code/FileRow.vue'
import LaunchpadHeader from './LaunchpadHeader.vue'
import type { CurrentStep } from './Wizard.vue'
import { ScaffoldedFilesFragment, ScaffoldedFiles_CompleteSetupDocument } from '../generated/graphql'
import { useMutation } from '@urql/vue'
const emit = defineEmits<{
(event: 'navigate', currentStep: CurrentStep): void
(event: 'completeSetup')
}>()

View File

@@ -13,7 +13,7 @@
v-for="opt in options"
:key="opt.type"
class="border-transparent border-1 py-4px px-12px hocus-default hocus:z-10 hover:bg-indigo-50 focus:outline-none first:rounded-l last:rounded-r"
:class="opt.id === value ? 'bg-secondary-50 text-secondary-600': 'text-gray-700'"
:class="opt.type === value ? 'bg-secondary-50 text-secondary-600': 'text-gray-700'"
@click="() => selectOption(opt.type)"
>
{{ opt.name }}

View File

@@ -8,14 +8,12 @@
<EnvironmentSetup
v-if="currentStep === 'selectFramework'"
:gql="props.gql.wizard"
:data="wizardSetupData"
@navigate="setCurrentStep"
@wizard-setup="onWizardSetup"
:next-fn="() => setCurrentStep('installDependencies')"
/>
<InstallDependencies
v-if="currentStep === 'installDependencies'"
:gql="props.gql"
@navigate="setCurrentStep"
:back-fn="() => setCurrentStep('selectFramework')"
/>
</div>
</template>
@@ -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<CurrentStep>('selectFramework')
const wizardSetupData = ref<WizardSetupData>({
bundler: props.gql.wizard.bundler?.type ?? null,
framework: props.gql.wizard.framework?.type ?? null,
codeLanguage: 'js',
})
const wizardUpdateMutation = useMutation(Wizard_WizardUpdateDocument)
const onWizardSetup = <K extends keyof WizardSetupData>(key: K, val: WizardSetupData[K]) => {
wizardSetupData.value[key] = val
wizardUpdateMutation.executeMutation({
input: wizardSetupData.value,
})
}
const setCurrentStep = (step: CurrentStep) => {
currentStep.value = step

View File

@@ -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/*"],

View File

@@ -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",

View File

@@ -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",

View File

@@ -0,0 +1,5 @@
module.exports = {
'stories': [
'../src/**/*.stories.@(js|jsx|ts|tsx)',
],
}

View File

@@ -0,0 +1,3 @@
module.exports = {
e2e: {},
}

View File

@@ -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"