Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(launchpad): update CT setup and config scaffolding #20893

Merged
merged 47 commits into from
Apr 14, 2022
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
bc6e107
wip
lmiller1990 Mar 31, 2022
01c38e1
wip
lmiller1990 Apr 1, 2022
b67daee
wip on ct tests
lmiller1990 Apr 1, 2022
915e911
refactor
lmiller1990 Apr 1, 2022
f6bf409
move wizard state to coreData
lmiller1990 Apr 3, 2022
31eb903
fix types
lmiller1990 Apr 4, 2022
b1c1f79
update ui
lmiller1990 Apr 4, 2022
c264393
merge in origin
lmiller1990 Apr 4, 2022
993b401
fix tests
lmiller1990 Apr 4, 2022
cd54481
fix tests
lmiller1990 Apr 4, 2022
5c336fe
fix lint
lmiller1990 Apr 4, 2022
892111b
fix tests
lmiller1990 Apr 4, 2022
91292c7
update e2e tests
lmiller1990 Apr 4, 2022
0979495
fix test
lmiller1990 Apr 4, 2022
4d19502
fixing tests
lmiller1990 Apr 4, 2022
2ae5a2a
fix globs
lmiller1990 Apr 4, 2022
179a9a8
remove accidental files
lmiller1990 Apr 4, 2022
4a0319c
add comment to test
lmiller1990 Apr 4, 2022
a90df0f
handle case of no global cypress
lmiller1990 Apr 4, 2022
834d056
fix bugs
lmiller1990 Apr 4, 2022
51ae6ab
fix test
lmiller1990 Apr 4, 2022
b7e5254
update versions
lmiller1990 Apr 4, 2022
7c27cc2
fix tests
lmiller1990 Apr 4, 2022
f0ff239
types
lmiller1990 Apr 4, 2022
b5f96bb
remove old code
lmiller1990 Apr 4, 2022
0da0882
merge in remote/10.0-release
lmiller1990 Apr 5, 2022
a531108
additional test coverage
lmiller1990 Apr 5, 2022
72168c0
add all deps for frameworks
lmiller1990 Apr 6, 2022
6970c74
Update packages/launchpad/src/setup/ManualInstall.vue
lmiller1990 Apr 6, 2022
389996b
Merge branch 'lmiller1990/UNIFY-1402' of https://github.com/cypress-i…
lmiller1990 Apr 6, 2022
0c8f9a3
fix tests
lmiller1990 Apr 6, 2022
f54860c
Merge remote-tracking branch 'origin/10.0-release' into lmiller1990/U…
lmiller1990 Apr 6, 2022
a4defbb
Merge remote-tracking branch 'origin/10.0-release' into lmiller1990/U…
lmiller1990 Apr 7, 2022
b487860
ensure deps are installed - do not skip
lmiller1990 Apr 7, 2022
95c55ce
remove old code
lmiller1990 Apr 7, 2022
f5a8537
typo
lmiller1990 Apr 7, 2022
83a48d7
merge in remote
lmiller1990 Apr 11, 2022
1992e75
update test
lmiller1990 Apr 11, 2022
1f65611
Merge branch '10.0-release' into lmiller1990/UNIFY-1402
tgriesser Apr 13, 2022
cc5d32b
remove superfulous try/catch
lmiller1990 Apr 13, 2022
10c7f92
feat: scaffold and register cy.mount command for CT (#20933)
lmiller1990 Apr 13, 2022
c190faf
Merge branch '10.0-release' into lmiller1990/UNIFY-1402
tgriesser Apr 14, 2022
6dfbfc5
one last file missed in merge
tgriesser Apr 14, 2022
9fabb91
see if this fixes percy errors?
tgriesser Apr 14, 2022
c87e9de
Merge branch '10.0-release' into lmiller1990/UNIFY-1402
ZachJW34 Apr 14, 2022
7bd810c
Fix types
tgriesser Apr 14, 2022
c51a34c
Merge branch '10.0-release' into lmiller1990/UNIFY-1402
ZachJW34 Apr 14, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions autobarrel.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
"prefix": "/* eslint-disable padding-line-between-statements */",
"paths": [
"packages/graphql/src/**/*",
"packages/data-context/src/**/*",
"packages/scaffold-config/src/**"
"packages/data-context/src/**/*"
],
"ignore": [
"packages/data-context/src/gen",
Expand Down
7 changes: 7 additions & 0 deletions npm/vite-dev-server-fresh/.percy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: 2
snapshot:
widths:
- 1280
min-height: 1024
discovery:
network-idle-timeout: 1000
4 changes: 2 additions & 2 deletions npm/vite-dev-server-fresh/cypress/e2e/react.cy.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/// <reference types="cypress" />
/// <reference path="../support/e2e.ts" />
import type { e2eProjectDirs } from '@packages/frontend-shared/cypress/e2e/support/e2eProjectDirs'
import type { fixtureDirs } from '@tooling/system-tests'

type ProjectDirs = typeof e2eProjectDirs
type ProjectDirs = typeof fixtureDirs

const VITE_REACT: ProjectDirs[number][] = ['vite2.8.6-react', 'vite2.9.1-react']

Expand Down
7 changes: 7 additions & 0 deletions npm/webpack-dev-server-fresh/.percy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: 2
snapshot:
widths:
- 1280
min-height: 1024
discovery:
network-idle-timeout: 1000
1 change: 0 additions & 1 deletion packages/data-context/src/DataContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,6 @@ export class DataContext {
await this.initializeOpenMode()
if (this.coreData.currentProject && this.coreData.currentTestingType && await this.lifecycleManager.waitForInitializeSuccess()) {
this.lifecycleManager.setAndLoadCurrentTestingType(this.coreData.currentTestingType)
this.lifecycleManager.scaffoldFilesIfNecessary()
}
} else {
throw new Error(`Missing DataContext config "mode" setting, expected run | open`)
Expand Down
20 changes: 0 additions & 20 deletions packages/data-context/src/actions/MigrationActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,26 +334,6 @@ export class MigrationActions {
}
}

async assertSuccessfulConfigScaffold (configFile: `cypress.config.${'js'|'ts'}`) {
assert(this.ctx.currentProject)

// we assert the generated configuration file against one from a project that has
// been verified to run correctly.
// each project has an `unconfigured` and `configured` variant in `system-tests/projects`
// for example vueclivue2-configured and vueclivue2-unconfigured.
// after setting the project up with the launchpad, the two projects should contain the same files.

const configuredProject = this.ctx.project.projectTitle(this.ctx.currentProject).replace('unconfigured', 'configured')
const expectedProjectConfig = path.join(__dirname, '..', '..', '..', '..', 'system-tests', 'projects', configuredProject, configFile)

const actual = formatConfig(await this.ctx.file.readFileInProject(configFile))
const expected = formatConfig(await this.ctx.fs.readFile(expectedProjectConfig, 'utf8'))

if (actual !== expected) {
throw Error(`Expected ${actual} to equal ${expected}`)
}
}

resetFlags () {
this.ctx.update((coreData) => {
const defaultFlags = makeCoreData().migration.flags
Expand Down
188 changes: 67 additions & 121 deletions packages/data-context/src/actions/WizardActions.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import type { CodeLanguageEnum, NexusGenEnums, NexusGenObjects } from '@packages/graphql/src/gen/nxs.gen'
import { CodeLanguage, CODE_LANGUAGES } from '@packages/types'
import { Bundler, FrontendFramework, FRONTEND_FRAMEWORKS, detect } from '@packages/scaffold-config'
import { CODE_LANGUAGES } from '@packages/types'
import { detect, WIZARD_FRAMEWORKS, WIZARD_BUNDLERS, commandsFileBody, supportFileComponent, supportFileE2E } from '@packages/scaffold-config'
import assert from 'assert'
import dedent from 'dedent'
import path from 'path'
import fs from 'fs-extra'
import Debug from 'debug'

const debug = Debug('cypress:data-context:wizard-actions')

import type { DataContext } from '..'

interface WizardGetCodeComponent {
chosenLanguage: CodeLanguage
chosenFramework: FrontendFramework
chosenLanguage: 'js' | 'ts'
chosenFramework: typeof WIZARD_FRAMEWORKS[number]
}

export class WizardActions {
Expand All @@ -29,13 +28,15 @@ export class WizardActions {
return this.ctx.wizardData
}

setFramework (framework: typeof FRONTEND_FRAMEWORKS[number]['type'] | null): void {
const next = FRONTEND_FRAMEWORKS.find((x) => x.type === framework)
setFramework (framework: typeof WIZARD_FRAMEWORKS[number] | null): void {
const next = WIZARD_FRAMEWORKS.find((x) => x.type === framework?.type)

this.ctx.coreData.wizard.chosenFramework = framework
this.ctx.update((coreData) => {
coreData.wizard.chosenFramework = framework
})

if (next?.supportedBundlers?.length === 1) {
this.setBundler(next?.supportedBundlers?.[0].type)
this.setBundler(next?.supportedBundlers?.[0])

return
}
Expand All @@ -45,26 +46,30 @@ export class WizardActions {
// if the previous bundler was incompatible with the
// new framework that was selected, we need to reset it
const doesNotSupportChosenBundler = (chosenBundler && !new Set(
this.ctx.wizard.chosenFramework?.supportedBundlers.map((x) => x.type) || [],
).has(chosenBundler)) ?? false
this.ctx.coreData.wizard.chosenFramework?.supportedBundlers.map((x) => x.type) || [],
).has(chosenBundler.type)) ?? false

const prevFramework = this.ctx.coreData.wizard.chosenFramework || ''
const prevFramework = this.ctx.coreData.wizard.chosenFramework?.type ?? null

if (doesNotSupportChosenBundler || !['react', 'vue'].includes(prevFramework)) {
if (!prevFramework || doesNotSupportChosenBundler || !['react', 'vue'].includes(prevFramework)) {
this.setBundler(null)
}
}

setBundler (bundler: Bundler | null) {
this.ctx.coreData.wizard.chosenBundler = bundler
setBundler (bundler: typeof WIZARD_BUNDLERS[number] | null) {
this.ctx.update((coreData) => {
coreData.wizard.chosenBundler = bundler
})

return this.data
return this.ctx.coreData.wizard
}

setCodeLanguage (lang: NexusGenEnums['CodeLanguageEnum']) {
this.ctx.coreData.wizard.chosenLanguage = lang
this.ctx.update((coreData) => {
coreData.wizard.chosenLanguage = lang
})

return this.data
return this.ctx.coreData.wizard
}

async completeSetup () {
Expand All @@ -83,48 +88,44 @@ export class WizardActions {

/// reset wizard status, useful for when changing to a new project
resetWizard () {
this.data.chosenBundler = null
this.data.chosenFramework = null
this.data.chosenLanguage = 'js'
this.ctx.update((coreData) => {
coreData.wizard.chosenBundler = null
coreData.wizard.chosenFramework = null
coreData.wizard.chosenLanguage = 'js'
coreData.wizard.detectedBundler = null
coreData.wizard.detectedFramework = null
})

return this.data
return this.ctx.coreData.wizard
}

async initialize () {
if (!this.ctx.currentProject) {
return
}

this.ctx.update((coreData) => {
coreData.wizard.detectedFramework = null
coreData.wizard.detectedBundler = null
coreData.wizard.detectedLanguage = null
})
this.resetWizard()

await this.detectLanguage()
debug('detectedLanguage %s', this.data.detectedLanguage)
this.data.chosenLanguage = this.data.detectedLanguage || 'js'

try {
const detected = detect(await fs.readJson(path.join(this.ctx.currentProject, 'package.json')))
const detected = detect(this.ctx.currentProject)

debug('detected %o', detected)
debug('detected %o', detected)

if (detected) {
this.ctx.update((coreData) => {
coreData.wizard.detectedFramework = detected.framework?.type ?? null
coreData.wizard.chosenFramework = detected.framework?.type ?? null
if (detected) {
this.ctx.update((coreData) => {
coreData.wizard.detectedFramework = detected.framework ?? null
coreData.wizard.chosenFramework = detected.framework ?? null

if (!detected.framework?.supportedBundlers[0]) {
return
}
if (!detected.framework?.supportedBundlers[0]) {
return
}

coreData.wizard.detectedBundler = detected.bundler || detected.framework.supportedBundlers[0].type
coreData.wizard.chosenBundler = detected.bundler || detected.framework.supportedBundlers[0].type
})
}
} catch {
// Could not detect anything - no problem, no need to do anything.
coreData.wizard.detectedBundler = detected.bundler || detected.framework.supportedBundlers[0]
coreData.wizard.chosenBundler = detected.bundler || detected.framework.supportedBundlers[0]
})
}
}

Expand Down Expand Up @@ -182,15 +183,15 @@ export class WizardActions {

private async scaffoldComponent () {
debug('scaffoldComponent')
const { chosenBundler, chosenFramework, chosenLanguage } = this.ctx.wizard
const { chosenBundler, chosenFramework, chosenLanguage } = this.ctx.coreData.wizard

assert(chosenFramework && chosenLanguage && chosenBundler)

return await Promise.all([
this.scaffoldConfig('component'),
this.scaffoldFixtures(),
this.scaffoldSupport('component', chosenLanguage.type),
this.scaffoldSupport('commands', chosenLanguage.type),
this.scaffoldSupport('component', chosenLanguage),
this.scaffoldSupport('commands', chosenLanguage),
this.getComponentIndexHtml({
chosenFramework,
chosenLanguage,
Expand All @@ -205,7 +206,18 @@ export class WizardActions {
// @ts-ignore
await this.ctx.fs.mkdir(supportDir, { recursive: true })

const fileContent = fileName === 'commands' ? this.commandsFileBody(language) : this.supportFileBody(fileName, language)
let fileContent: string | undefined

if (fileName === 'commands') {
fileContent = commandsFileBody(language)
} else if (fileName === 'e2e') {
fileContent = supportFileE2E(language)
} else if (fileName === 'component') {
assert(this.ctx.coreData.wizard.chosenFramework)
fileContent = supportFileComponent(language, this.ctx.coreData.wizard.chosenFramework)
}

assert(fileContent)

await this.scaffoldFile(supportFile, fileContent, 'Scaffold default support file')

Expand All @@ -222,11 +234,16 @@ export class WizardActions {
if (testingType === 'component') {
const chosenLanguage = CODE_LANGUAGES.find((f) => f.type === language)

const { chosenBundler, chosenFramework } = this.ctx.wizard
const { chosenBundler, chosenFramework } = this.ctx.coreData.wizard

assert(chosenFramework && chosenLanguage && chosenBundler)
assert(chosenFramework && chosenLanguage && chosenBundler && this.ctx.currentProject)

return chosenFramework.config[chosenLanguage.type](chosenBundler.type)
return chosenFramework.createCypressConfig({
language: chosenLanguage.type,
bundler: chosenBundler.type,
framework: chosenFramework.configFramework,
projectRoot: this.ctx.currentProject,
})
}

return this.wizardGetConfigCodeE2E(language)
Expand Down Expand Up @@ -382,79 +399,8 @@ export class WizardActions {
private ensureDir (type: 'component' | 'e2e' | 'fixtures') {
return this.ctx.fs.ensureDir(path.join(this.projectRoot, 'cypress', type))
}

private supportFileBody (fileName: 'e2e' | 'component', language: CodeLanguageEnum) {
return dedent`
// ***********************************************************
// This example support/${fileName}.${language} is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')
`
}

private commandsFileBody (language: CodeLanguageEnum) {
return dedent`
${language === 'ts' ? '/// <reference types="cypress" />' : ''}
// ***********************************************
// This example commands.${language} shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
${language === 'ts' ? COMMAND_TYPES : ''}
`
}
}

const COMMAND_TYPES = dedent`
//
// declare global {
// namespace Cypress {
// interface Chainable {
// login(email: string, password: string): Chainable<void>
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }
`

const E2E_SCAFFOLD_BODY = dedent`
e2e: {
setupNodeEvents(on, config) {
Expand Down
9 changes: 0 additions & 9 deletions packages/data-context/src/data/ProjectLifecycleManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ export interface InjectedConfigApi {
}

export interface ProjectMetaState {
hasFrontendFramework: 'nuxt' | 'react' | 'react-scripts' | 'vue' | 'next' | false
hasTypescript: boolean
hasLegacyCypressJson: boolean
hasCypressEnvFile: boolean
Expand All @@ -62,7 +61,6 @@ export interface ProjectMetaState {
}

const PROJECT_META_STATE: ProjectMetaState = {
hasFrontendFramework: false,
hasTypescript: false,
hasLegacyCypressJson: false,
allFoundConfigFiles: [],
Expand Down Expand Up @@ -598,13 +596,6 @@ export class ProjectLifecycleManager {
if (packageJson.dependencies?.typescript || packageJson.devDependencies?.typescript || fs.existsSync(this._pathToFile('tsconfig.json'))) {
metaState.hasTypescript = true
}

for (const framework of ['next', 'nuxt', 'react-scripts', 'react', 'vue'] as const) {
if (packageJson.dependencies?.[framework] || packageJson.devDependencies?.[framework]) {
metaState.hasFrontendFramework = framework
break
}
}
} catch {
// No need to handle
}
Expand Down
Loading