From b920973b0bbe65e3a81dd43eee561a378fcd0ef9 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Fri, 3 Feb 2023 12:48:35 +1000 Subject: [PATCH 01/40] chore: refactoring and types --- .../src/actions/CodegenActions.ts | 4 +- .../data-context/src/actions/WizardActions.ts | 11 +- .../src/data/ProjectConfigManager.ts | 8 +- .../test/unit/codegen/code-generator.spec.ts | 4 +- .../test/unit/codegen/spec-options.spec.ts | 16 +-- .../unit/sources/WizardDataSource.spec.ts | 4 +- .../schemaTypes/enumTypes/gql-WizardEnums.ts | 4 +- .../schemaTypes/objectTypes/gql-Mutation.ts | 4 +- .../src/schemaTypes/objectTypes/gql-Wizard.ts | 4 +- packages/scaffold-config/src/dependencies.ts | 47 +++++- packages/scaffold-config/src/detect.ts | 8 +- packages/scaffold-config/src/frameworks.ts | 134 ++++++++++++++++-- .../unit/component-index-template.spec.ts | 4 +- 13 files changed, 208 insertions(+), 44 deletions(-) diff --git a/packages/data-context/src/actions/CodegenActions.ts b/packages/data-context/src/actions/CodegenActions.ts index 2f81836b7e3..dc27ba4618e 100644 --- a/packages/data-context/src/actions/CodegenActions.ts +++ b/packages/data-context/src/actions/CodegenActions.ts @@ -5,7 +5,7 @@ import type { DataContext } from '..' import { SpecOptions, codeGenerator } from '../codegen' import templates from '../codegen/templates' import type { CodeGenType } from '../gen/graphcache-config.gen' -import { WizardFrontendFramework, WIZARD_FRAMEWORKS } from '@packages/scaffold-config' +import { WizardFrontendFramework, CT_FRAMEWORKS } from '@packages/scaffold-config' import { parse as parseReactComponent, resolver as reactDocgenResolvers } from 'react-docgen' import { visit } from 'ast-types' @@ -161,7 +161,7 @@ export class CodegenActions { } // @ts-ignore - because of the conditional above, we know that devServer isn't a function - return WIZARD_FRAMEWORKS.find((framework) => framework.configFramework === config?.component?.devServer.framework) + return CT_FRAMEWORKS.find((framework) => framework.configFramework === config?.component?.devServer.framework) } } diff --git a/packages/data-context/src/actions/WizardActions.ts b/packages/data-context/src/actions/WizardActions.ts index 4436a865b7f..afa237e8d2d 100644 --- a/packages/data-context/src/actions/WizardActions.ts +++ b/packages/data-context/src/actions/WizardActions.ts @@ -1,5 +1,5 @@ import type { NexusGenObjects } from '@packages/graphql/src/gen/nxs.gen' -import { detectFramework, WIZARD_FRAMEWORKS, commandsFileBody, supportFileComponent, supportFileE2E, WizardBundler, WizardFrontendFramework } from '@packages/scaffold-config' +import { detectFramework, CT_FRAMEWORKS, commandsFileBody, supportFileComponent, supportFileE2E, WizardBundler, WizardFrontendFramework } from '@packages/scaffold-config' import assert from 'assert' import path from 'path' import Debug from 'debug' @@ -9,6 +9,7 @@ const debug = Debug('cypress:data-context:wizard-actions') import type { DataContext } from '..' import { addTestingTypeToCypressConfig, AddTestingTypeToCypressConfigOptions } from '@packages/config' +import componentIndexHtmlGenerator from '@packages/scaffold-config/src/component-index-template' export class WizardActions { constructor (private ctx: DataContext) {} @@ -24,13 +25,13 @@ export class WizardActions { } setFramework (framework: WizardFrontendFramework | null): void { - const next = WIZARD_FRAMEWORKS.find((x) => x.type === framework?.type) + const next = CT_FRAMEWORKS.find((x) => x.type === framework?.type) this.ctx.update((coreData) => { coreData.wizard.chosenFramework = framework }) - if (next?.supportedBundlers?.length === 1) { + if (next?.supportedBundlers?.[0]) { this.setBundler(next?.supportedBundlers?.[0]) return @@ -300,9 +301,11 @@ export class WizardActions { await this.ensureDir('support') + const defaultComponentIndex = componentIndexHtmlGenerator('') + return this.scaffoldFile( componentIndexHtmlPath, - chosenFramework.componentIndexHtml(), + chosenFramework.componentIndexHtml?.() ?? defaultComponentIndex(), 'The HTML wrapper that each component is served with. Used for global fonts, CSS, JS, HTML, etc.', ) } diff --git a/packages/data-context/src/data/ProjectConfigManager.ts b/packages/data-context/src/data/ProjectConfigManager.ts index 753d4c5de16..3be0fcca866 100644 --- a/packages/data-context/src/data/ProjectConfigManager.ts +++ b/packages/data-context/src/data/ProjectConfigManager.ts @@ -18,7 +18,7 @@ import { CypressEnv } from './CypressEnv' import { autoBindDebug } from '../util/autoBindDebug' import type { EventRegistrar } from './EventRegistrar' import type { DataContext } from '../DataContext' -import { DependencyToInstall, isDependencyInstalled, WIZARD_BUNDLERS, WIZARD_DEPENDENCIES, WIZARD_FRAMEWORKS } from '@packages/scaffold-config' +import { DependencyToInstall, isDependencyInstalled, WIZARD_BUNDLERS, CT_FRAMEWORKS } from '@packages/scaffold-config' const debug = debugLib(`cypress:lifecycle:ProjectConfigManager`) @@ -191,7 +191,7 @@ export class ProjectConfigManager { return } - const isFrameworkSatisfied = async (bundler: typeof WIZARD_BUNDLERS[number], framework: typeof WIZARD_FRAMEWORKS[number]) => { + const isFrameworkSatisfied = async (bundler: typeof WIZARD_BUNDLERS[number], framework: typeof CT_FRAMEWORKS[number]) => { for (const dep of await (framework.dependencies(bundler.type, this.options.projectRoot))) { const res = await isDependencyInstalled(dep.dependency, this.options.projectRoot) @@ -203,9 +203,9 @@ export class ProjectConfigManager { return true } - const frameworks = WIZARD_FRAMEWORKS.filter((x) => x.configFramework === devServerOptions.framework) + const frameworks = CT_FRAMEWORKS.filter((x) => x.configFramework === devServerOptions.framework) - const mismatchedFrameworkDeps = new Map() + const mismatchedFrameworkDeps = new Map() let isSatisfied = false diff --git a/packages/data-context/test/unit/codegen/code-generator.spec.ts b/packages/data-context/test/unit/codegen/code-generator.spec.ts index be244325e82..484e0956dff 100644 --- a/packages/data-context/test/unit/codegen/code-generator.spec.ts +++ b/packages/data-context/test/unit/codegen/code-generator.spec.ts @@ -10,7 +10,7 @@ import { import { SpecOptions } from '../../../src/codegen/spec-options' import templates from '../../../src/codegen/templates' import { createTestDataContext } from '../helper' -import { WIZARD_FRAMEWORKS } from '@packages/scaffold-config' +import { CT_FRAMEWORKS } from '@packages/scaffold-config' import { defaultSpecPattern } from '@packages/config' const tmpPath = path.join(__dirname, 'tmp/test-code-gen') @@ -352,7 +352,7 @@ describe('code-generator', () => { currentProject: 'path/to/myProject', codeGenPath: path.join(__dirname, 'files', 'react', 'Button.jsx'), codeGenType: 'component', - framework: WIZARD_FRAMEWORKS[1], + framework: CT_FRAMEWORKS[1], isDefaultSpecPattern: true, specPattern: [defaultSpecPattern.component], }) diff --git a/packages/data-context/test/unit/codegen/spec-options.spec.ts b/packages/data-context/test/unit/codegen/spec-options.spec.ts index 083f8530377..1027ca06a02 100644 --- a/packages/data-context/test/unit/codegen/spec-options.spec.ts +++ b/packages/data-context/test/unit/codegen/spec-options.spec.ts @@ -1,5 +1,5 @@ import { defaultSpecPattern } from '@packages/config' -import { WIZARD_FRAMEWORKS } from '@packages/scaffold-config' +import { CT_FRAMEWORKS } from '@packages/scaffold-config' import { expect } from 'chai' import fs from 'fs-extra' import path from 'path' @@ -89,7 +89,7 @@ describe('spec-options', () => { codeGenPath: `${tmpPath}/MyComponent.vue`, codeGenType: 'component', isDefaultSpecPattern: true, - framework: WIZARD_FRAMEWORKS[1], + framework: CT_FRAMEWORKS[1], specPattern: [defaultSpecPattern.component], }) @@ -108,7 +108,7 @@ describe('spec-options', () => { codeGenPath: `${tmpPath}/MyComponent.vue`, codeGenType: 'component', isDefaultSpecPattern: true, - framework: WIZARD_FRAMEWORKS[1], + framework: CT_FRAMEWORKS[1], specPattern: [defaultSpecPattern.component], }) @@ -127,7 +127,7 @@ describe('spec-options', () => { codeGenPath: `${tmpPath}/Counter.tsx`, codeGenType: 'component', isDefaultSpecPattern: true, - framework: WIZARD_FRAMEWORKS[0], + framework: CT_FRAMEWORKS[0], specPattern: [defaultSpecPattern.component], componentName: 'Counter', isDefault: true, @@ -146,7 +146,7 @@ describe('spec-options', () => { codeGenPath: `${tmpPath}/Counter.tsx`, codeGenType: 'component', isDefaultSpecPattern: true, - framework: WIZARD_FRAMEWORKS[0], + framework: CT_FRAMEWORKS[0], specPattern: [defaultSpecPattern.component], componentName: 'View', }) @@ -166,7 +166,7 @@ describe('spec-options', () => { codeGenPath: `${tmpPath}/Counter.tsx`, codeGenType: 'component', isDefaultSpecPattern: true, - framework: WIZARD_FRAMEWORKS[0], + framework: CT_FRAMEWORKS[0], specPattern: [defaultSpecPattern.component], componentName: 'View', }) @@ -212,7 +212,7 @@ describe('spec-options', () => { codeGenPath: `${tmpPath}/${componentPath}`, codeGenType: 'component', isDefaultSpecPattern: false, - framework: WIZARD_FRAMEWORKS[1], + framework: CT_FRAMEWORKS[1], specPattern, specs, }) @@ -322,7 +322,7 @@ describe('spec-options', () => { codeGenType: 'component', isDefaultSpecPattern: true, specPattern: [defaultSpecPattern.component], - framework: WIZARD_FRAMEWORKS[1], + framework: CT_FRAMEWORKS[1], }) await fs.outputFile(`${tmpPath}/${fileName}`, '// foo') diff --git a/packages/data-context/test/unit/sources/WizardDataSource.spec.ts b/packages/data-context/test/unit/sources/WizardDataSource.spec.ts index 6bb6367d887..d2d29a3b201 100644 --- a/packages/data-context/test/unit/sources/WizardDataSource.spec.ts +++ b/packages/data-context/test/unit/sources/WizardDataSource.spec.ts @@ -1,9 +1,9 @@ -import { WizardBundler, WizardFrontendFramework, WIZARD_BUNDLERS, WIZARD_FRAMEWORKS } from '@packages/scaffold-config' +import { WizardBundler, WizardFrontendFramework, WIZARD_BUNDLERS, CT_FRAMEWORKS } from '@packages/scaffold-config' import { expect } from 'chai' import { createTestDataContext, scaffoldMigrationProject, removeCommonNodeModules } from '../helper' function findFramework (type: WizardFrontendFramework['type']) { - return WIZARD_FRAMEWORKS.find((x) => x.type === type)! + return CT_FRAMEWORKS.find((x) => x.type === type)! } function findBundler (type: WizardBundler['type']) { diff --git a/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts b/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts index 02f03f3a0c9..8f8652878ea 100644 --- a/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts +++ b/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts @@ -1,5 +1,5 @@ import { CODE_LANGUAGES } from '@packages/types' -import { WIZARD_FRAMEWORKS, WIZARD_BUNDLERS } from '@packages/scaffold-config' +import { CT_FRAMEWORKS, WIZARD_BUNDLERS } from '@packages/scaffold-config' import { enumType } from 'nexus' export const SupportedBundlerEnum = enumType({ @@ -15,7 +15,7 @@ export const WizardConfigFileStatusEnum = enumType({ export const FrontendFrameworkEnum = enumType({ name: 'FrontendFrameworkEnum', - members: WIZARD_FRAMEWORKS.map((t) => t.type), + members: CT_FRAMEWORKS.map((t) => t.type), }) export const SupportStatusEnum = enumType({ diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts index daf950a4ea0..ba1b8cfe265 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts @@ -10,7 +10,7 @@ import { GenerateSpecResponse } from './gql-GenerateSpecResponse' import { Cohort, CohortInput } from './gql-Cohorts' import { Query } from './gql-Query' import { ScaffoldedFile } from './gql-ScaffoldedFile' -import { WIZARD_BUNDLERS, WIZARD_FRAMEWORKS } from '@packages/scaffold-config' +import { WIZARD_BUNDLERS, CT_FRAMEWORKS } from '@packages/scaffold-config' import debugLib from 'debug' import { ReactComponentResponse } from './gql-ReactComponentResponse' import { TestsBySpecInput } from '../inputTypes' @@ -218,7 +218,7 @@ export const mutation = mutationType({ }, resolve: async (source, args, ctx) => { if (args.input.framework) { - ctx.actions.wizard.setFramework(WIZARD_FRAMEWORKS.find((x) => x.type === args.input.framework) ?? null) + ctx.actions.wizard.setFramework(CT_FRAMEWORKS.find((x) => x.type === args.input.framework) ?? null) } if (args.input.bundler) { diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-Wizard.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-Wizard.ts index 65092d21a6b..872a350e81b 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-Wizard.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-Wizard.ts @@ -2,7 +2,7 @@ import { WizardBundler } from './gql-WizardBundler' import { WizardFrontendFramework } from './gql-WizardFrontendFramework' import { WizardNpmPackage } from './gql-WizardNpmPackage' import { objectType } from 'nexus' -import { WIZARD_BUNDLERS, WIZARD_FRAMEWORKS } from '@packages/scaffold-config' +import { WIZARD_BUNDLERS, CT_FRAMEWORKS } from '@packages/scaffold-config' export const Wizard = objectType({ name: 'Wizard', @@ -27,7 +27,7 @@ export const Wizard = objectType({ t.nonNull.list.nonNull.field('frameworks', { type: WizardFrontendFramework, description: 'All of the component testing frameworks to choose from', - resolve: () => Array.from(WIZARD_FRAMEWORKS), // TODO(tim): fix this in nexus to accept Readonly + resolve: () => Array.from(CT_FRAMEWORKS), // TODO(tim): fix this in nexus to accept Readonly }) t.nonNull.list.nonNull.field('packagesToInstall', { diff --git a/packages/scaffold-config/src/dependencies.ts b/packages/scaffold-config/src/dependencies.ts index 0367f0aef11..d48fc429d83 100644 --- a/packages/scaffold-config/src/dependencies.ts +++ b/packages/scaffold-config/src/dependencies.ts @@ -1,3 +1,48 @@ +export interface CypressComponentDependency { + /** + * Unique idenitifer. + * @example 'reactscripts' + */ + type: string + + /** + * Name to display in the user interface. + * @example "React Scripts" + */ + name: string + + /** + * Package name on npm. + * @example react-scripts + */ + package: string + + /** + * Code to run when installing. Version is optional. + * + * Should be @. + * + * @example `react` + * @example `react@18` + * @example `react-scripts` + */ + installer: string + + /** + * Description shown in UI. It is recommended to use the same one the package uses on npm. + * @example 'Create React apps with no build configuration' + */ + description: string + + /** + * Minimum version supported. Should conform to Semantic Versioning as used in `package.json`. + * @see https://docs.npmjs.com/cli/v9/configuring-npm/package-json#dependencies + * @example '^=4.0.0 || ^=5.0.0' + * @example '^2.0.0' + */ + minVersion: string +} + export const WIZARD_DEPENDENCY_WEBPACK = { type: 'webpack', name: 'Webpack', @@ -142,7 +187,7 @@ export const WIZARD_DEPENDENCY_ANGULAR_PLATFORM_BROWSER_DYNAMIC = { minVersion: '^=13.0.0 || ^=14.0.0 || ^=15.0.0', } as const -export const WIZARD_DEPENDENCY_SVELTE = { +export const WIZARD_DEPENDENCY_SVELTE: CypressComponentDependency = { type: 'svelte', name: 'Svelte.js', package: 'svelte', diff --git a/packages/scaffold-config/src/detect.ts b/packages/scaffold-config/src/detect.ts index 7c2be16935c..6b93bd9363f 100644 --- a/packages/scaffold-config/src/detect.ts +++ b/packages/scaffold-config/src/detect.ts @@ -1,4 +1,4 @@ -import { WIZARD_FRAMEWORKS, isDependencyInstalled, WizardFrontendFramework, WizardBundler } from './frameworks' +import { CT_FRAMEWORKS, isDependencyInstalled, WizardFrontendFramework, WizardBundler } from './frameworks' import { WIZARD_BUNDLERS } from './dependencies' import path from 'path' import fs from 'fs' @@ -13,7 +13,7 @@ interface DetectFramework { bundler?: WizardBundler } -export async function areAllDepsSatisified (projectPath: string, framework: typeof WIZARD_FRAMEWORKS[number]) { +export async function areAllDepsSatisified (projectPath: string, framework: typeof CT_FRAMEWORKS[number]) { for (const dep of framework.detectors) { const result = await isDependencyInstalled(dep, projectPath) @@ -34,7 +34,7 @@ export async function areAllDepsSatisified (projectPath: string, framework: type // and the bundler, which could be Vite. export async function detectFramework (projectPath: string): Promise { // first see if it's a template - for (const framework of WIZARD_FRAMEWORKS.filter((x) => x.category === 'template')) { + for (const framework of CT_FRAMEWORKS.filter((x) => x.category === 'template')) { const hasAllDeps = await areAllDepsSatisified(projectPath, framework) // so far all the templates we support only have 1 bundler, @@ -51,7 +51,7 @@ export async function detectFramework (projectPath: string): Promise x.category === 'library')) { + for (const library of CT_FRAMEWORKS.filter((x) => x.category === 'library')) { // multiple bundlers supported, eg React works with webpack and Vite. // try to infer which one they are using. const hasLibrary = await areAllDepsSatisified(projectPath, library) diff --git a/packages/scaffold-config/src/frameworks.ts b/packages/scaffold-config/src/frameworks.ts index 1922e08efb6..16ac6be7a08 100644 --- a/packages/scaffold-config/src/frameworks.ts +++ b/packages/scaffold-config/src/frameworks.ts @@ -2,26 +2,25 @@ import path from 'path' import fs from 'fs-extra' import * as dependencies from './dependencies' import componentIndexHtmlGenerator from './component-index-template' +import type { CypressComponentDependency } from './dependencies' import semver from 'semver' export type PkgJson = { version: string, dependencies?: Record, devDependencies?: Record } export type WizardBundler = typeof dependencies.WIZARD_BUNDLERS[number] -export type CodeGenFramework = typeof WIZARD_FRAMEWORKS[number]['codeGenFramework'] - -export type WizardDependency = typeof dependencies.WIZARD_DEPENDENCIES[number] +export type CodeGenFramework = typeof CT_FRAMEWORKS[number]['codeGenFramework'] export interface DependencyToInstall { - dependency: WizardDependency + dependency: CypressComponentDependency satisfied: boolean loc: string | null detectedVersion: string | null } -export type WizardFrontendFramework = typeof WIZARD_FRAMEWORKS[number] & { specPattern?: string } +export type WizardFrontendFramework = typeof CT_FRAMEWORKS[number] & { specPattern?: string } -export async function isDependencyInstalled (dependency: WizardDependency, projectPath: string): Promise { +export async function isDependencyInstalled (dependency: CypressComponentDependency, projectPath: string): Promise { try { const loc = require.resolve(path.join(dependency.package, 'package.json'), { paths: [projectPath], @@ -61,7 +60,7 @@ function getBundlerDependency (bundler: WizardBundler['type'], projectPath: stri } } -export type WizardMountModule = Awaited> +export type WizardMountModule = Awaited> const mountModule = (mountModule: T) => (projectPath: string) => Promise.resolve(mountModule) @@ -75,7 +74,124 @@ const reactMountModule = async (projectPath: string) => { return semver.major(reactPkg.detectedVersion) === 18 ? 'cypress/react18' : 'cypress/react' } -export const WIZARD_FRAMEWORKS = [ +interface ComponentFrameworkDefinition { + /** + * A semantic, unique identifier. + * Example: 'reactscripts', 'nextjs' + */ + type: string + + /** + * Used as the flag for `getPreset` for meta framworks, such as finding the webpack config for CRA, Angular, etc. + * @see https://github.com/cypress-io/cypress/blob/ad0b2a37c2fe587f4efe4920d2e445eca5301600/npm/webpack-dev-server/src/devServer.ts#L119 + * This could be extended to be a function that a user can inject, eg: + * + * configFramwork: () => { + * return getSolidJsMetaFrameworkBundlerConfig() + * } + * It is also the name of the string added to `cypress.config` + * + * @example + * + * export default { + * component: { + * devServer: { + * framework: 'solid' // can be 'next', 'create-react-app', etc etc. + * } + * } + * } + */ + configFramework: string // 'create-react-app', + + /** + * Library (React, Vue) or template (aka "meta framework") (CRA, Next.js, Angular) + */ + category: 'library' | 'template' + + /** + * Implement a similar interface to https://github.com/cypress-io/cypress/blob/ad0b2a37c2fe587f4efe4920d2e445eca5301600/npm/webpack-dev-server/src/devServer.ts#L117 + * + * Only required for `category: framework`. + * + * @cypress/webpack-dev-server will need updating to receive custom `devServerHandler`. + * @cypress/vite-dev-server does not currently support the concept of a framework config preset yet, but this can be added. + * + * NOTE: This could be a "fast follow" if we want to reduce the scope of this brief. + */ + getDevServerConfig?: () => Promise<{ frameworkConfig: any }> + + /** + * Name displayed in Launchpad when doing initial setup. + * @example 'Solid.js', 'Create React App' + */ + name: string + + /** + * Supported bundlers. + */ + supportedBundlers: Array + + /** + * Used to attempt to automatically select the correct framework/bundler from the dropdown. + * @example + * + * const SOLID_DETECTOR: Dependency = { + * type: 'solid', + * name: 'Solid.js', + * package: 'solid-js', + * installer: 'solid-js', + * description: 'Solid is a declarative JavaScript library for creating user interfaces', + * minVersion: '^1.0.0', + * } + */ + detectors: CypressComponentDependency[] + + /** + * Array of required dependencies. This could be the bundler and JavaScript library. + * It's the same type as `detectors`. + */ + dependencies: (bundler: WizardBundler['type'], projectPath: string) => Promise + // dependencies: () => Promise + + /** + * @internal + * This is used interally by Cypress for the "Create From Component" feature. + * @example 'react' + */ + codeGenFramework: string + + /** + * @internal + * This is used interally by Cypress for the "Create From Component" feature. + * @example '*.{js,jsx,tsx}' + */ + glob: string + + /** + * This is the path to get mount, eg `import { mount } from , + * @example: `cypress-ct-solidjs/src/mount` + */ + mountModule: (projectPath: string) => Promise + + /** + * Support status. Internally alpha | beta | full. + * Community integrations are "community". + */ + supportStatus: 'alpha' | 'beta' | 'full' | 'community' + + /** + * Function returning string for used for the component-index.html file. + * Cypress provides a default if one isn't specified for third party integrations. + */ + componentIndexHtml?: () => string + + /** + * @internal + */ + specPattern?: '**/*.cy.ts' +} + +export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ { type: 'reactscripts', configFramework: 'create-react-app', @@ -280,4 +396,4 @@ export const WIZARD_FRAMEWORKS = [ supportStatus: 'alpha', componentIndexHtml: componentIndexHtmlGenerator(), }, -] as const +] diff --git a/packages/scaffold-config/test/unit/component-index-template.spec.ts b/packages/scaffold-config/test/unit/component-index-template.spec.ts index 75801e05db3..9e28d2b7f6e 100644 --- a/packages/scaffold-config/test/unit/component-index-template.spec.ts +++ b/packages/scaffold-config/test/unit/component-index-template.spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai' import dedent from 'dedent' import componentIndexHtmlGenerator from '../../src/component-index-template' -import { WIZARD_FRAMEWORKS } from '../../src/frameworks' +import { CT_FRAMEWORKS } from '../../src/frameworks' describe('componentIndexHtmlGenerator', () => { it('strips spaces and newlines appropriately', () => { @@ -46,7 +46,7 @@ describe('componentIndexHtmlGenerator', () => { }) it('generates correct template for Next.js', () => { - const nextjs = WIZARD_FRAMEWORKS.find((x) => x.name === 'Next.js')! + const nextjs = CT_FRAMEWORKS.find((x) => x.name === 'Next.js')! const actual = nextjs.componentIndexHtml() From 9af7df50c278c96bb39e28755a4fbeaad7ccfd11 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Fri, 3 Feb 2023 13:36:50 +1000 Subject: [PATCH 02/40] rework source of frameworks --- .../src/actions/CodegenActions.ts | 6 +- .../data-context/src/actions/WizardActions.ts | 10 +-- .../data-context/src/codegen/spec-options.ts | 4 +- .../src/data/ProjectConfigManager.ts | 7 +- .../data-context/src/data/coreDataShape.ts | 8 +- .../unit/sources/WizardDataSource.spec.ts | 4 +- .../support/mock-graphql/clientTestContext.ts | 4 +- .../support/mock-graphql/stubgql-Wizard.ts | 2 +- packages/graphql/schemas/schema.graphql | 73 ++++++++----------- .../schemaTypes/enumTypes/gql-WizardEnums.ts | 7 +- .../inputTypes/gql-WizardUpdateInput.ts | 6 +- .../schemaTypes/objectTypes/gql-Mutation.ts | 4 +- .../src/schemaTypes/objectTypes/gql-Wizard.ts | 10 +-- .../gql-WizardFrontendFramework.ts | 11 ++- .../cypress/e2e/scaffold-project.cy.ts | 6 +- .../launchpad/src/setup/SelectFwOrBundler.vue | 5 +- packages/launchpad/src/utils/icons.ts | 4 +- packages/scaffold-config/src/detect.ts | 12 +-- packages/scaffold-config/src/frameworks.ts | 10 +-- packages/scaffold-config/src/supportFile.ts | 3 +- .../unit/component-index-template.spec.ts | 2 +- .../cache/darwin/snapshot-meta.json | 2 +- .../cache/linux/snapshot-meta.json | 2 +- .../cache/win32/snapshot-meta.json | 2 +- 24 files changed, 90 insertions(+), 114 deletions(-) diff --git a/packages/data-context/src/actions/CodegenActions.ts b/packages/data-context/src/actions/CodegenActions.ts index dc27ba4618e..52c41ee0c4c 100644 --- a/packages/data-context/src/actions/CodegenActions.ts +++ b/packages/data-context/src/actions/CodegenActions.ts @@ -5,7 +5,7 @@ import type { DataContext } from '..' import { SpecOptions, codeGenerator } from '../codegen' import templates from '../codegen/templates' import type { CodeGenType } from '../gen/graphcache-config.gen' -import { WizardFrontendFramework, CT_FRAMEWORKS } from '@packages/scaffold-config' +import type { ComponentFrameworkDefinition } from '@packages/scaffold-config' import { parse as parseReactComponent, resolver as reactDocgenResolvers } from 'react-docgen' import { visit } from 'ast-types' @@ -152,7 +152,7 @@ export class CodegenActions { }) } - getWizardFrameworkFromConfig (): WizardFrontendFramework | undefined { + getWizardFrameworkFromConfig (): ComponentFrameworkDefinition | undefined { const config = this.ctx.lifecycleManager.loadedConfigFile // If devServer is a function, they are using a custom dev server. @@ -161,7 +161,7 @@ export class CodegenActions { } // @ts-ignore - because of the conditional above, we know that devServer isn't a function - return CT_FRAMEWORKS.find((framework) => framework.configFramework === config?.component?.devServer.framework) + return this.ctx.coreData.wizard.frameworks.find((framework) => framework.configFramework === config?.component?.devServer.framework) } } diff --git a/packages/data-context/src/actions/WizardActions.ts b/packages/data-context/src/actions/WizardActions.ts index afa237e8d2d..83475ec1ced 100644 --- a/packages/data-context/src/actions/WizardActions.ts +++ b/packages/data-context/src/actions/WizardActions.ts @@ -1,5 +1,5 @@ import type { NexusGenObjects } from '@packages/graphql/src/gen/nxs.gen' -import { detectFramework, CT_FRAMEWORKS, commandsFileBody, supportFileComponent, supportFileE2E, WizardBundler, WizardFrontendFramework } from '@packages/scaffold-config' +import { detectFramework, commandsFileBody, supportFileComponent, supportFileE2E, WizardBundler, ComponentFrameworkDefinition } from '@packages/scaffold-config' import assert from 'assert' import path from 'path' import Debug from 'debug' @@ -24,8 +24,8 @@ export class WizardActions { return this.ctx.wizardData } - setFramework (framework: WizardFrontendFramework | null): void { - const next = CT_FRAMEWORKS.find((x) => x.type === framework?.type) + setFramework (framework: ComponentFrameworkDefinition | null): void { + const next = this.ctx.coreData.wizard.frameworks.find((x) => x.type === framework?.type) this.ctx.update((coreData) => { coreData.wizard.chosenFramework = framework @@ -88,7 +88,7 @@ export class WizardActions { this.resetWizard() - const detected = await detectFramework(this.ctx.currentProject) + const detected = await detectFramework(this.ctx.currentProject, this.ctx.coreData.wizard.frameworks) debug('detected %o', detected) @@ -296,7 +296,7 @@ export class WizardActions { } } - private async scaffoldComponentIndexHtml (chosenFramework: WizardFrontendFramework): Promise { + private async scaffoldComponentIndexHtml (chosenFramework: ComponentFrameworkDefinition): Promise { const componentIndexHtmlPath = path.join(this.projectRoot, 'cypress', 'support', 'component-index.html') await this.ensureDir('support') diff --git a/packages/data-context/src/codegen/spec-options.ts b/packages/data-context/src/codegen/spec-options.ts index dea6618626f..ddde2eef181 100644 --- a/packages/data-context/src/codegen/spec-options.ts +++ b/packages/data-context/src/codegen/spec-options.ts @@ -1,6 +1,6 @@ import type { ParsedPath } from 'path' import type { CodeGenType } from '@packages/graphql/src/gen/nxs.gen' -import type { WizardFrontendFramework } from '@packages/scaffold-config' +import type { ComponentFrameworkDefinition } from '@packages/scaffold-config' import fs from 'fs-extra' import { uniq, upperFirst } from 'lodash' import path from 'path' @@ -14,7 +14,7 @@ interface CodeGenOptions { isDefaultSpecPattern: boolean specPattern: string[] currentProject: string | null - framework?: WizardFrontendFramework + framework?: ComponentFrameworkDefinition specs?: FoundSpec[] componentName?: string isDefault?: boolean diff --git a/packages/data-context/src/data/ProjectConfigManager.ts b/packages/data-context/src/data/ProjectConfigManager.ts index 3be0fcca866..f0f1b8d2403 100644 --- a/packages/data-context/src/data/ProjectConfigManager.ts +++ b/packages/data-context/src/data/ProjectConfigManager.ts @@ -18,7 +18,7 @@ import { CypressEnv } from './CypressEnv' import { autoBindDebug } from '../util/autoBindDebug' import type { EventRegistrar } from './EventRegistrar' import type { DataContext } from '../DataContext' -import { DependencyToInstall, isDependencyInstalled, WIZARD_BUNDLERS, CT_FRAMEWORKS } from '@packages/scaffold-config' +import { DependencyToInstall, isDependencyInstalled, WIZARD_BUNDLERS, ComponentFrameworkDefinition } from '@packages/scaffold-config' const debug = debugLib(`cypress:lifecycle:ProjectConfigManager`) @@ -191,7 +191,7 @@ export class ProjectConfigManager { return } - const isFrameworkSatisfied = async (bundler: typeof WIZARD_BUNDLERS[number], framework: typeof CT_FRAMEWORKS[number]) => { + const isFrameworkSatisfied = async (bundler: typeof WIZARD_BUNDLERS[number], framework: ComponentFrameworkDefinition) => { for (const dep of await (framework.dependencies(bundler.type, this.options.projectRoot))) { const res = await isDependencyInstalled(dep.dependency, this.options.projectRoot) @@ -203,7 +203,8 @@ export class ProjectConfigManager { return true } - const frameworks = CT_FRAMEWORKS.filter((x) => x.configFramework === devServerOptions.framework) + // hm, not sure about this + const frameworks = this.options.ctx.coreData.wizard.frameworks.filter((x) => x.configFramework === devServerOptions.framework) const mismatchedFrameworkDeps = new Map() diff --git a/packages/data-context/src/data/coreDataShape.ts b/packages/data-context/src/data/coreDataShape.ts index 8f6c66e85c4..e368d4f3e8f 100644 --- a/packages/data-context/src/data/coreDataShape.ts +++ b/packages/data-context/src/data/coreDataShape.ts @@ -1,5 +1,5 @@ import { FoundBrowser, Editor, AllowedState, AllModeOptions, TestingType, BrowserStatus, PACKAGE_MANAGERS, AuthStateName, MIGRATION_STEPS, MigrationStep, BannerState } from '@packages/types' -import type { WizardFrontendFramework, WizardBundler } from '@packages/scaffold-config' +import { WizardBundler, ComponentFrameworkDefinition, CT_FRAMEWORKS } from '@packages/scaffold-config' import type { NexusGenObjects } from '@packages/graphql/src/gen/nxs.gen' import type { App, BrowserWindow } from 'electron' import type { ChildProcess } from 'child_process' @@ -66,10 +66,11 @@ export interface AppDataShape { export interface WizardDataShape { chosenBundler: WizardBundler | null - chosenFramework: WizardFrontendFramework | null + chosenFramework: ComponentFrameworkDefinition | null chosenManualInstall: boolean detectedBundler: WizardBundler | null - detectedFramework: WizardFrontendFramework | null + detectedFramework: ComponentFrameworkDefinition | null + frameworks: ComponentFrameworkDefinition[] } export interface MigrationDataShape { @@ -197,6 +198,7 @@ export function makeCoreData (modeOptions: Partial = {}): CoreDa chosenManualInstall: false, detectedBundler: null, detectedFramework: null, + frameworks: CT_FRAMEWORKS, }, migration: { step: 'renameAuto', diff --git a/packages/data-context/test/unit/sources/WizardDataSource.spec.ts b/packages/data-context/test/unit/sources/WizardDataSource.spec.ts index d2d29a3b201..8b4ffad643a 100644 --- a/packages/data-context/test/unit/sources/WizardDataSource.spec.ts +++ b/packages/data-context/test/unit/sources/WizardDataSource.spec.ts @@ -1,8 +1,8 @@ -import { WizardBundler, WizardFrontendFramework, WIZARD_BUNDLERS, CT_FRAMEWORKS } from '@packages/scaffold-config' +import { WizardBundler, ComponentFrameworkDefinition, WIZARD_BUNDLERS, CT_FRAMEWORKS } from '@packages/scaffold-config' import { expect } from 'chai' import { createTestDataContext, scaffoldMigrationProject, removeCommonNodeModules } from '../helper' -function findFramework (type: WizardFrontendFramework['type']) { +function findFramework (type: ComponentFrameworkDefinition['type']) { return CT_FRAMEWORKS.find((x) => x.type === type)! } diff --git a/packages/frontend-shared/cypress/support/mock-graphql/clientTestContext.ts b/packages/frontend-shared/cypress/support/mock-graphql/clientTestContext.ts index f0076368fe0..8fc94cb51c2 100644 --- a/packages/frontend-shared/cypress/support/mock-graphql/clientTestContext.ts +++ b/packages/frontend-shared/cypress/support/mock-graphql/clientTestContext.ts @@ -3,7 +3,7 @@ import type { CurrentProject, Browser, WizardBundler, - WizardFrontendFramework, + ComponentFrameworkDefinition, GlobalProject, VersionData, LocalSettings, @@ -26,7 +26,7 @@ export interface ClientTestContext { localSettings: LocalSettings wizard: { chosenBundler: WizardBundler | null - chosenFramework: WizardFrontendFramework | null + chosenFramework: ComponentFrameworkDefinition | null chosenManualInstall: boolean allBundlers: WizardBundler[] warnings: [] 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 9055eb33b26..edb26d327ec 100644 --- a/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Wizard.ts +++ b/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Wizard.ts @@ -53,7 +53,7 @@ export const stubWizard: MaybeResolver = { allBundlers, frameworks: testFrameworks.map(({ name, type, supportedBundlers, category, supportStatus }, idx) => { return { - ...testNodeId('WizardFrontendFramework'), + ...testNodeId('ComponentFrameworkDefinition'), supportStatus, name, category, diff --git a/packages/graphql/schemas/schema.graphql b/packages/graphql/schemas/schema.graphql index c9a8cbf6292..cf3ebaa4aa0 100644 --- a/packages/graphql/schemas/schema.graphql +++ b/packages/graphql/schemas/schema.graphql @@ -885,6 +885,33 @@ input CohortInput { weights: [Int!] } +"""A frontend framework that we can setup within the app""" +type ComponentFrameworkDefinition implements Node { + """The category (framework, like react-scripts, or library, like react""" + category: String! + + """Relay style Node ID field for the ComponentFrameworkDefinition field""" + id: ID! + + """Whether this is the detected framework""" + isDetected: Boolean! + + """Whether this is the selected framework in the wizard""" + isSelected: Boolean! + + """The display name of the framework""" + name: String! + + """Current support status of the framework""" + supportStatus: SupportStatusEnum! + + """All of the supported bundlers for this framework""" + supportedBundlers: [WizardBundler!]! + + """The unique identifier for a framework or library""" + type: String! +} + """ The currently opened Cypress project, represented by a cypress.config.{js,ts,mjs,cjs} file """ @@ -1276,19 +1303,6 @@ type FileParts implements Node { relative: String! } -enum FrontendFrameworkEnum { - angular - nextjs - nuxtjs - react - reactscripts - svelte - vue2 - vue3 - vueclivue2 - vueclivue3 -} - """Error from generated spec""" type GenerateSpecResponse { """The currently opened project""" @@ -2307,10 +2321,10 @@ type Wizard { """All of the bundlers to choose from""" allBundlers: [WizardBundler!]! bundler: WizardBundler - framework: WizardFrontendFramework + framework: ComponentFrameworkDefinition """All of the component testing frameworks to choose from""" - frameworks: [WizardFrontendFramework!]! + frameworks: [ComponentFrameworkDefinition!]! """Command to install required command""" installDependenciesCommand: String @@ -2343,33 +2357,6 @@ enum WizardConfigFileStatusEnum { valid } -"""A frontend framework that we can setup within the app""" -type WizardFrontendFramework implements Node { - """The category (framework, like react-scripts, or library, like react""" - category: String! - - """Relay style Node ID field for the WizardFrontendFramework field""" - id: ID! - - """Whether this is the detected framework""" - isDetected: Boolean! - - """Whether this is the selected framework in the wizard""" - isSelected: Boolean! - - """The display name of the framework""" - name: String! - - """Current support status of the framework""" - supportStatus: SupportStatusEnum! - - """All of the supported bundlers for this framework""" - supportedBundlers: [WizardBundler!]! - - """The unique identifier for a framework or library""" - type: FrontendFrameworkEnum! -} - """Details about an NPM Package listed during the wizard install""" type WizardNpmPackage implements Node { """Short description about the purpose of the package""" @@ -2398,5 +2385,5 @@ type WizardNpmPackage implements Node { input WizardUpdateInput { bundler: SupportedBundlers - framework: FrontendFrameworkEnum + framework: String } diff --git a/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts b/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts index 8f8652878ea..41624f5853f 100644 --- a/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts +++ b/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts @@ -1,5 +1,5 @@ import { CODE_LANGUAGES } from '@packages/types' -import { CT_FRAMEWORKS, WIZARD_BUNDLERS } from '@packages/scaffold-config' +import { WIZARD_BUNDLERS } from '@packages/scaffold-config' import { enumType } from 'nexus' export const SupportedBundlerEnum = enumType({ @@ -13,11 +13,6 @@ export const WizardConfigFileStatusEnum = enumType({ members: ['changes', 'valid', 'skipped', 'error'], }) -export const FrontendFrameworkEnum = enumType({ - name: 'FrontendFrameworkEnum', - members: CT_FRAMEWORKS.map((t) => t.type), -}) - export const SupportStatusEnum = enumType({ name: 'SupportStatusEnum', members: ['alpha', 'beta', 'full'], diff --git a/packages/graphql/src/schemaTypes/inputTypes/gql-WizardUpdateInput.ts b/packages/graphql/src/schemaTypes/inputTypes/gql-WizardUpdateInput.ts index 988ca5d4300..dcc0a2587fd 100644 --- a/packages/graphql/src/schemaTypes/inputTypes/gql-WizardUpdateInput.ts +++ b/packages/graphql/src/schemaTypes/inputTypes/gql-WizardUpdateInput.ts @@ -1,12 +1,10 @@ import { inputObjectType } from 'nexus' -import { FrontendFrameworkEnum, SupportedBundlerEnum } from '../enumTypes/gql-WizardEnums' +import { SupportedBundlerEnum } from '../enumTypes/gql-WizardEnums' export const WizardUpdateInput = inputObjectType({ name: 'WizardUpdateInput', definition (t) { - t.field('framework', { - type: FrontendFrameworkEnum, - }) + t.string('framework') t.field('bundler', { type: SupportedBundlerEnum, diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts index ba1b8cfe265..61c16dafb1b 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts @@ -10,7 +10,7 @@ import { GenerateSpecResponse } from './gql-GenerateSpecResponse' import { Cohort, CohortInput } from './gql-Cohorts' import { Query } from './gql-Query' import { ScaffoldedFile } from './gql-ScaffoldedFile' -import { WIZARD_BUNDLERS, CT_FRAMEWORKS } from '@packages/scaffold-config' +import { WIZARD_BUNDLERS } from '@packages/scaffold-config' import debugLib from 'debug' import { ReactComponentResponse } from './gql-ReactComponentResponse' import { TestsBySpecInput } from '../inputTypes' @@ -218,7 +218,7 @@ export const mutation = mutationType({ }, resolve: async (source, args, ctx) => { if (args.input.framework) { - ctx.actions.wizard.setFramework(CT_FRAMEWORKS.find((x) => x.type === args.input.framework) ?? null) + ctx.actions.wizard.setFramework(ctx.coreData.wizard.frameworks.find((x) => x.type === args.input.framework) ?? null) } if (args.input.bundler) { diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-Wizard.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-Wizard.ts index 872a350e81b..abc14fec71c 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-Wizard.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-Wizard.ts @@ -1,8 +1,8 @@ import { WizardBundler } from './gql-WizardBundler' -import { WizardFrontendFramework } from './gql-WizardFrontendFramework' +import { ComponentFrameworkDefinition } from './gql-WizardFrontendFramework' import { WizardNpmPackage } from './gql-WizardNpmPackage' import { objectType } from 'nexus' -import { WIZARD_BUNDLERS, CT_FRAMEWORKS } from '@packages/scaffold-config' +import { WIZARD_BUNDLERS } from '@packages/scaffold-config' export const Wizard = objectType({ name: 'Wizard', @@ -20,14 +20,14 @@ export const Wizard = objectType({ }) t.field('framework', { - type: WizardFrontendFramework, + type: ComponentFrameworkDefinition, resolve: (source, args, ctx) => ctx.coreData.wizard.chosenFramework ?? null, }) t.nonNull.list.nonNull.field('frameworks', { - type: WizardFrontendFramework, + type: ComponentFrameworkDefinition, description: 'All of the component testing frameworks to choose from', - resolve: () => Array.from(CT_FRAMEWORKS), // TODO(tim): fix this in nexus to accept Readonly + resolve: (source, args, ctx) => Array.from(ctx.coreData.wizard.frameworks), }) t.nonNull.list.nonNull.field('packagesToInstall', { diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts index 50a34855fd4..eab9f4ddd80 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts @@ -1,14 +1,13 @@ import { WizardBundler } from './gql-WizardBundler' -import { FrontendFrameworkEnum, SupportStatusEnum } from '../enumTypes/gql-WizardEnums' +import { SupportStatusEnum } from '../enumTypes/gql-WizardEnums' import { objectType } from 'nexus' -export const WizardFrontendFramework = objectType({ - name: 'WizardFrontendFramework', +export const ComponentFrameworkDefinition = objectType({ + name: 'ComponentFrameworkDefinition', description: 'A frontend framework that we can setup within the app', node: 'type', definition (t) { - t.nonNull.field('type', { - type: FrontendFrameworkEnum, + t.nonNull.string('type', { description: 'The unique identifier for a framework or library', }), @@ -47,6 +46,6 @@ export const WizardFrontendFramework = objectType({ sourceType: { module: '@packages/scaffold-config', - export: 'WizardFrontendFramework', + export: 'ComponentFrameworkDefinition', }, }) diff --git a/packages/launchpad/cypress/e2e/scaffold-project.cy.ts b/packages/launchpad/cypress/e2e/scaffold-project.cy.ts index 92085d5e9e1..79386b9beed 100644 --- a/packages/launchpad/cypress/e2e/scaffold-project.cy.ts +++ b/packages/launchpad/cypress/e2e/scaffold-project.cy.ts @@ -1,4 +1,4 @@ -import type { WizardFrontendFramework } from '@packages/scaffold-config' +import type { ComponentFrameworkDefinition } from '@packages/scaffold-config' import type { SnapshotScaffoldTestResult } from '@packages/launchpad/cypress/tasks/snapshotsScaffold' // The tests in this file take an existing project without Cypress Configured @@ -56,8 +56,8 @@ function scaffoldAndOpenE2EProject (opts: { function scaffoldAndOpenCTProject (opts: { name: Parameters[0] - framework: WizardFrontendFramework['name'] - bundler?: WizardFrontendFramework['supportedBundlers'][number]['name'] + framework: ComponentFrameworkDefinition['name'] + bundler?: ComponentFrameworkDefinition['supportedBundlers'][number]['name'] args?: Parameters[1] removeFixturesFolder?: boolean }) { diff --git a/packages/launchpad/src/setup/SelectFwOrBundler.vue b/packages/launchpad/src/setup/SelectFwOrBundler.vue index 8fd5947bb00..682d9dc62b1 100644 --- a/packages/launchpad/src/setup/SelectFwOrBundler.vue +++ b/packages/launchpad/src/setup/SelectFwOrBundler.vue @@ -73,7 +73,7 @@ interface RootOption { } interface FrameworkOption extends RootOption { - type: FrontendFrameworkEnum + type: string supportStatus: 'alpha' | 'beta' | 'full' } @@ -90,7 +90,6 @@ import { computed, ref } from 'vue' import { FrameworkBundlerLogos } from '../utils/icons' import Select from '@cy/components/Select.vue' import type { - FrontendFrameworkEnum, SupportedBundlers, } from '../generated/graphql' import { useI18n } from '@cy/i18n' @@ -113,7 +112,7 @@ const props = withDefaults(defineProps<{ }) const emit = defineEmits<{ - (event: 'selectFramework', type: FrontendFrameworkEnum) + (event: 'selectFramework', type: string) (event: 'selectBundler', type: SupportedBundlers) }>() diff --git a/packages/launchpad/src/utils/icons.ts b/packages/launchpad/src/utils/icons.ts index 709ae2e5d24..cf800b4e4e7 100644 --- a/packages/launchpad/src/utils/icons.ts +++ b/packages/launchpad/src/utils/icons.ts @@ -7,9 +7,9 @@ import LogoReact from '../images/logos/react.svg' import LogoAngular from '../images/logos/angular.svg' import LogoSvelte from '../images/logos/svelte.svg' -import type { FrontendFrameworkEnum, SupportedBundlers } from '../generated/graphql' +import type { SupportedBundlers } from '../generated/graphql' -export const FrameworkBundlerLogos: Record = { +export const FrameworkBundlerLogos: Record = { webpack: LogoWebpack, vite: LogoVite, vue2: LogoVue, diff --git a/packages/scaffold-config/src/detect.ts b/packages/scaffold-config/src/detect.ts index 6b93bd9363f..a7ce7b82518 100644 --- a/packages/scaffold-config/src/detect.ts +++ b/packages/scaffold-config/src/detect.ts @@ -1,4 +1,4 @@ -import { CT_FRAMEWORKS, isDependencyInstalled, WizardFrontendFramework, WizardBundler } from './frameworks' +import { isDependencyInstalled, WizardBundler, ComponentFrameworkDefinition } from './frameworks' import { WIZARD_BUNDLERS } from './dependencies' import path from 'path' import fs from 'fs' @@ -9,11 +9,11 @@ import Debug from 'debug' const debug = Debug('cypress:scaffold-config:detect') interface DetectFramework { - framework?: WizardFrontendFramework + framework?: ComponentFrameworkDefinition bundler?: WizardBundler } -export async function areAllDepsSatisified (projectPath: string, framework: typeof CT_FRAMEWORKS[number]) { +export async function areAllDepsSatisified (projectPath: string, framework: ComponentFrameworkDefinition) { for (const dep of framework.detectors) { const result = await isDependencyInstalled(dep, projectPath) @@ -32,9 +32,9 @@ export async function areAllDepsSatisified (projectPath: string, framework: type // If we don't find a specific framework, but we do find a library and/or // bundler, we return both the framework, which might just be "React", // and the bundler, which could be Vite. -export async function detectFramework (projectPath: string): Promise { +export async function detectFramework (projectPath: string, frameworks: ComponentFrameworkDefinition[]): Promise { // first see if it's a template - for (const framework of CT_FRAMEWORKS.filter((x) => x.category === 'template')) { + for (const framework of frameworks.filter((x) => x.category === 'template')) { const hasAllDeps = await areAllDepsSatisified(projectPath, framework) // so far all the templates we support only have 1 bundler, @@ -51,7 +51,7 @@ export async function detectFramework (projectPath: string): Promise x.category === 'library')) { + for (const library of frameworks.filter((x) => x.category === 'library')) { // multiple bundlers supported, eg React works with webpack and Vite. // try to infer which one they are using. const hasLibrary = await areAllDepsSatisified(projectPath, library) diff --git a/packages/scaffold-config/src/frameworks.ts b/packages/scaffold-config/src/frameworks.ts index 16ac6be7a08..209c8fe12d9 100644 --- a/packages/scaffold-config/src/frameworks.ts +++ b/packages/scaffold-config/src/frameworks.ts @@ -9,7 +9,7 @@ export type PkgJson = { version: string, dependencies?: Record, export type WizardBundler = typeof dependencies.WIZARD_BUNDLERS[number] -export type CodeGenFramework = typeof CT_FRAMEWORKS[number]['codeGenFramework'] +export type CodeGenFramework = ComponentFrameworkDefinition['codeGenFramework'] export interface DependencyToInstall { dependency: CypressComponentDependency @@ -18,8 +18,6 @@ export interface DependencyToInstall { detectedVersion: string | null } -export type WizardFrontendFramework = typeof CT_FRAMEWORKS[number] & { specPattern?: string } - export async function isDependencyInstalled (dependency: CypressComponentDependency, projectPath: string): Promise { try { const loc = require.resolve(path.join(dependency.package, 'package.json'), { @@ -60,8 +58,6 @@ function getBundlerDependency (bundler: WizardBundler['type'], projectPath: stri } } -export type WizardMountModule = Awaited> - const mountModule = (mountModule: T) => (projectPath: string) => Promise.resolve(mountModule) const reactMountModule = async (projectPath: string) => { @@ -74,7 +70,7 @@ const reactMountModule = async (projectPath: string) => { return semver.major(reactPkg.detectedVersion) === 18 ? 'cypress/react18' : 'cypress/react' } -interface ComponentFrameworkDefinition { +export interface ComponentFrameworkDefinition { /** * A semantic, unique identifier. * Example: 'reactscripts', 'nextjs' @@ -158,7 +154,7 @@ interface ComponentFrameworkDefinition { * This is used interally by Cypress for the "Create From Component" feature. * @example 'react' */ - codeGenFramework: string + codeGenFramework: 'react' | 'vue' | 'svelte' | 'angular' /** * @internal diff --git a/packages/scaffold-config/src/supportFile.ts b/packages/scaffold-config/src/supportFile.ts index 9fdefcb5f54..19f5f7871d8 100644 --- a/packages/scaffold-config/src/supportFile.ts +++ b/packages/scaffold-config/src/supportFile.ts @@ -1,6 +1,5 @@ import type { CodeLanguage } from '@packages/types' import dedent from 'dedent' -import type { WizardMountModule } from './frameworks' export function supportFileE2E (language: CodeLanguage['type']) { return dedent` @@ -27,7 +26,7 @@ export function supportFileE2E (language: CodeLanguage['type']) { ` } -export function supportFileComponent (language: CodeLanguage['type'], mountModule: WizardMountModule) { +export function supportFileComponent (language: CodeLanguage['type'], mountModule: string) { const supportFileTemplate = dedent` // *********************************************************** // This example support/component.${language} is processed and diff --git a/packages/scaffold-config/test/unit/component-index-template.spec.ts b/packages/scaffold-config/test/unit/component-index-template.spec.ts index 9e28d2b7f6e..6c658b5d08c 100644 --- a/packages/scaffold-config/test/unit/component-index-template.spec.ts +++ b/packages/scaffold-config/test/unit/component-index-template.spec.ts @@ -48,7 +48,7 @@ describe('componentIndexHtmlGenerator', () => { it('generates correct template for Next.js', () => { const nextjs = CT_FRAMEWORKS.find((x) => x.name === 'Next.js')! - const actual = nextjs.componentIndexHtml() + const actual = nextjs.componentIndexHtml?.() const expected = dedent` diff --git a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json index b242581d5fb..99775ad5f9b 100644 --- a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json @@ -3646,7 +3646,7 @@ "./packages/graphql/src/schemaTypes/objectTypes/gql-VersionData.ts", "./packages/graphql/src/schemaTypes/objectTypes/gql-Wizard.ts", "./packages/graphql/src/schemaTypes/objectTypes/gql-WizardBundler.ts", - "./packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts", + "./packages/graphql/src/schemaTypes/objectTypes/gql-ComponentFrameworkDefinition.ts", "./packages/graphql/src/schemaTypes/objectTypes/gql-WizardNpmPackage.ts", "./packages/graphql/src/schemaTypes/unions/gql-GeneratedSpecResult.ts", "./packages/graphql/src/schemaTypes/unions/index.ts", diff --git a/tooling/v8-snapshot/cache/linux/snapshot-meta.json b/tooling/v8-snapshot/cache/linux/snapshot-meta.json index f2aa9ed66e1..4dd95f16d44 100644 --- a/tooling/v8-snapshot/cache/linux/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/linux/snapshot-meta.json @@ -3857,7 +3857,7 @@ "./packages/graphql/src/schemaTypes/objectTypes/gql-VersionData.ts", "./packages/graphql/src/schemaTypes/objectTypes/gql-Wizard.ts", "./packages/graphql/src/schemaTypes/objectTypes/gql-WizardBundler.ts", - "./packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts", + "./packages/graphql/src/schemaTypes/objectTypes/gql-ComponentFrameworkDefinition.ts", "./packages/graphql/src/schemaTypes/objectTypes/gql-WizardNpmPackage.ts", "./packages/graphql/src/schemaTypes/unions/gql-GeneratedSpecResult.ts", "./packages/graphql/src/schemaTypes/unions/index.ts", diff --git a/tooling/v8-snapshot/cache/win32/snapshot-meta.json b/tooling/v8-snapshot/cache/win32/snapshot-meta.json index 94fa7f987f9..989844aa929 100644 --- a/tooling/v8-snapshot/cache/win32/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/win32/snapshot-meta.json @@ -3855,7 +3855,7 @@ "./packages/graphql/src/schemaTypes/objectTypes/gql-VersionData.ts", "./packages/graphql/src/schemaTypes/objectTypes/gql-Wizard.ts", "./packages/graphql/src/schemaTypes/objectTypes/gql-WizardBundler.ts", - "./packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts", + "./packages/graphql/src/schemaTypes/objectTypes/gql-ComponentFrameworkDefinition.ts", "./packages/graphql/src/schemaTypes/objectTypes/gql-WizardNpmPackage.ts", "./packages/graphql/src/schemaTypes/unions/gql-GeneratedSpecResult.ts", "./packages/graphql/src/schemaTypes/unions/index.ts", From b0e6a54ed9904e3a62422f60f70f7213c54427ec Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Fri, 3 Feb 2023 14:09:39 +1000 Subject: [PATCH 03/40] revert rename --- packages/graphql/schemas/schema.graphql | 58 +++++++++---------- .../src/schemaTypes/objectTypes/gql-Wizard.ts | 6 +- .../gql-WizardFrontendFramework.ts | 4 +- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/packages/graphql/schemas/schema.graphql b/packages/graphql/schemas/schema.graphql index cf3ebaa4aa0..39d9435a440 100644 --- a/packages/graphql/schemas/schema.graphql +++ b/packages/graphql/schemas/schema.graphql @@ -885,33 +885,6 @@ input CohortInput { weights: [Int!] } -"""A frontend framework that we can setup within the app""" -type ComponentFrameworkDefinition implements Node { - """The category (framework, like react-scripts, or library, like react""" - category: String! - - """Relay style Node ID field for the ComponentFrameworkDefinition field""" - id: ID! - - """Whether this is the detected framework""" - isDetected: Boolean! - - """Whether this is the selected framework in the wizard""" - isSelected: Boolean! - - """The display name of the framework""" - name: String! - - """Current support status of the framework""" - supportStatus: SupportStatusEnum! - - """All of the supported bundlers for this framework""" - supportedBundlers: [WizardBundler!]! - - """The unique identifier for a framework or library""" - type: String! -} - """ The currently opened Cypress project, represented by a cypress.config.{js,ts,mjs,cjs} file """ @@ -2321,10 +2294,10 @@ type Wizard { """All of the bundlers to choose from""" allBundlers: [WizardBundler!]! bundler: WizardBundler - framework: ComponentFrameworkDefinition + framework: WizardFrontendFramework """All of the component testing frameworks to choose from""" - frameworks: [ComponentFrameworkDefinition!]! + frameworks: [WizardFrontendFramework!]! """Command to install required command""" installDependenciesCommand: String @@ -2357,6 +2330,33 @@ enum WizardConfigFileStatusEnum { valid } +"""A frontend framework that we can setup within the app""" +type WizardFrontendFramework implements Node { + """The category (framework, like react-scripts, or library, like react""" + category: String! + + """Relay style Node ID field for the WizardFrontendFramework field""" + id: ID! + + """Whether this is the detected framework""" + isDetected: Boolean! + + """Whether this is the selected framework in the wizard""" + isSelected: Boolean! + + """The display name of the framework""" + name: String! + + """Current support status of the framework""" + supportStatus: SupportStatusEnum! + + """All of the supported bundlers for this framework""" + supportedBundlers: [WizardBundler!]! + + """The unique identifier for a framework or library""" + type: String! +} + """Details about an NPM Package listed during the wizard install""" type WizardNpmPackage implements Node { """Short description about the purpose of the package""" diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-Wizard.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-Wizard.ts index abc14fec71c..dae2afb2bcb 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-Wizard.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-Wizard.ts @@ -1,5 +1,5 @@ import { WizardBundler } from './gql-WizardBundler' -import { ComponentFrameworkDefinition } from './gql-WizardFrontendFramework' +import { WizardFrontendFramework } from './gql-WizardFrontendFramework' import { WizardNpmPackage } from './gql-WizardNpmPackage' import { objectType } from 'nexus' import { WIZARD_BUNDLERS } from '@packages/scaffold-config' @@ -20,12 +20,12 @@ export const Wizard = objectType({ }) t.field('framework', { - type: ComponentFrameworkDefinition, + type: WizardFrontendFramework, resolve: (source, args, ctx) => ctx.coreData.wizard.chosenFramework ?? null, }) t.nonNull.list.nonNull.field('frameworks', { - type: ComponentFrameworkDefinition, + type: WizardFrontendFramework, description: 'All of the component testing frameworks to choose from', resolve: (source, args, ctx) => Array.from(ctx.coreData.wizard.frameworks), }) diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts index eab9f4ddd80..ae0dc8601a4 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts @@ -2,8 +2,8 @@ import { WizardBundler } from './gql-WizardBundler' import { SupportStatusEnum } from '../enumTypes/gql-WizardEnums' import { objectType } from 'nexus' -export const ComponentFrameworkDefinition = objectType({ - name: 'ComponentFrameworkDefinition', +export const WizardFrontendFramework = objectType({ + name: 'WizardFrontendFramework', description: 'A frontend framework that we can setup within the app', node: 'type', definition (t) { From 8685358c68aea1c09ac3f92a07fe361083c1b171 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Mon, 6 Feb 2023 12:01:34 +1000 Subject: [PATCH 04/40] fix tests --- .../scaffold-config/test/unit/detect.spec.ts | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/scaffold-config/test/unit/detect.spec.ts b/packages/scaffold-config/test/unit/detect.spec.ts index b1514f34bc7..341bea0f8ba 100644 --- a/packages/scaffold-config/test/unit/detect.spec.ts +++ b/packages/scaffold-config/test/unit/detect.spec.ts @@ -3,7 +3,7 @@ import { expect } from 'chai' import fs from 'fs-extra' import globby from 'globby' import type { ProjectFixtureDir } from '@tooling/system-tests' -import { detectFramework, detectLanguage, PkgJson } from '../../src' +import { detectFramework, detectLanguage, PkgJson, CT_FRAMEWORKS } from '../../src' import Fixtures from '@tooling/system-tests' import path from 'path' @@ -76,7 +76,7 @@ describe('detectFramework', () => { const projectPath = await scaffoldMigrationProject('create-react-app-unconfigured') fakeDepsInNodeModules(projectPath, [{ dependency: 'react-scripts', version: '5.0.0' }]) - const actual = await detectFramework(projectPath) + const actual = await detectFramework(projectPath, CT_FRAMEWORKS) expect(actual.framework?.type).to.eq('reactscripts') }) @@ -85,7 +85,7 @@ describe('detectFramework', () => { const projectPath = await scaffoldMigrationProject('create-react-app-unconfigured') fakeDepsInNodeModules(projectPath, [{ dependency: 'react-scripts', version: '4.0.0' }]) - const actual = await detectFramework(projectPath) + const actual = await detectFramework(projectPath, CT_FRAMEWORKS) expect(actual.framework?.type).to.eq('reactscripts') }) @@ -98,7 +98,7 @@ describe('detectFramework', () => { { devDependency: 'webpack', version: '5.0.0' }, ]) - const actual = await detectFramework(projectPath) + const actual = await detectFramework(projectPath, CT_FRAMEWORKS) expect(actual.framework?.type).to.eq('react') expect(actual.bundler?.type).to.eq('webpack') @@ -112,7 +112,7 @@ describe('detectFramework', () => { { dependency: 'vue', version: '2.5.0' }, ]) - const actual = await detectFramework(projectPath) + const actual = await detectFramework(projectPath, CT_FRAMEWORKS) expect(actual.framework?.type).to.eq('vueclivue2') expect(actual.bundler?.type).to.eq('webpack') @@ -126,7 +126,7 @@ describe('detectFramework', () => { { dependency: 'vue', version: '3.2.0' }, ]) - const actual = await detectFramework(projectPath) + const actual = await detectFramework(projectPath, CT_FRAMEWORKS) expect(actual.framework?.type).to.eq('vueclivue3') expect(actual.bundler?.type).to.eq('webpack') @@ -140,7 +140,7 @@ describe('detectFramework', () => { { dependency: 'vue', version: '3.2.0' }, ]) - const actual = await detectFramework(projectPath) + const actual = await detectFramework(projectPath, CT_FRAMEWORKS) expect(actual.framework?.type).to.eq('vueclivue3') expect(actual.bundler?.type).to.eq('webpack') @@ -154,7 +154,7 @@ describe('detectFramework', () => { { dependency: 'react', version: '17.0.0' }, ]) - const actual = await detectFramework(projectPath) + const actual = await detectFramework(projectPath, CT_FRAMEWORKS) expect(actual.framework?.type).to.eq('react') expect(actual.bundler?.type).to.eq('vite') @@ -168,7 +168,7 @@ describe('detectFramework', () => { { dependency: 'react', version: '17.0.0' }, ]) - const actual = await detectFramework(projectPath) + const actual = await detectFramework(projectPath, CT_FRAMEWORKS) expect(actual.framework?.type).to.eq('react') expect(actual.bundler?.type).to.eq('vite') @@ -182,7 +182,7 @@ describe('detectFramework', () => { { dependency: 'vue', version: '3.0.0' }, ]) - const actual = await detectFramework(projectPath) + const actual = await detectFramework(projectPath, CT_FRAMEWORKS) expect(actual.framework?.type).to.eq('vue3') expect(actual.bundler?.type).to.eq('vite') @@ -197,7 +197,7 @@ describe('detectFramework', () => { { dependency: 'next', version: v }, ]) - const actual = await detectFramework(projectPath) + const actual = await detectFramework(projectPath, CT_FRAMEWORKS) expect(actual.framework?.type).to.eq('nextjs') expect(actual.bundler?.type).to.eq('webpack') @@ -212,7 +212,7 @@ describe('detectFramework', () => { { dependency: '@angular/cli', version: v }, ]) - const actual = await detectFramework(projectPath) + const actual = await detectFramework(projectPath, CT_FRAMEWORKS) expect(actual.framework?.type).to.eq('angular') expect(actual.bundler?.type).to.eq('webpack') @@ -228,7 +228,7 @@ describe('detectFramework', () => { { dependency: 'vite', version: v }, ]) - const actual = await detectFramework(projectPath) + const actual = await detectFramework(projectPath, CT_FRAMEWORKS) expect(actual.framework?.type).to.eq('svelte') expect(actual.bundler?.type).to.eq('vite') @@ -243,7 +243,7 @@ describe('detectFramework', () => { { dependency: 'webpack', version: '5.0.0' }, ]) - const actual = await detectFramework(projectPath) + const actual = await detectFramework(projectPath, CT_FRAMEWORKS) expect(actual.framework?.type).to.eq('svelte') expect(actual.bundler?.type).to.eq('webpack') @@ -257,7 +257,7 @@ describe('detectFramework', () => { // monorepo like situations where there can be multiple levels of // node_modules above the projectPath. fs.rmSync(path.join(Fixtures.cyTmpDir, 'node_modules'), { recursive: true, force: true }) - const actual = await detectFramework(projectPath) + const actual = await detectFramework(projectPath, CT_FRAMEWORKS) expect(actual.framework).to.be.undefined expect(actual.bundler).to.be.undefined From bd446d7283d89d2d00476276b713b75f42579a54 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Mon, 6 Feb 2023 13:28:21 +1000 Subject: [PATCH 05/40] fix more tests --- packages/data-context/src/actions/WizardActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/data-context/src/actions/WizardActions.ts b/packages/data-context/src/actions/WizardActions.ts index 83475ec1ced..21afd26e11e 100644 --- a/packages/data-context/src/actions/WizardActions.ts +++ b/packages/data-context/src/actions/WizardActions.ts @@ -31,7 +31,7 @@ export class WizardActions { coreData.wizard.chosenFramework = framework }) - if (next?.supportedBundlers?.[0]) { + if (next?.supportedBundlers?.[0] && next.supportedBundlers.length === 1) { this.setBundler(next?.supportedBundlers?.[0]) return From 169fd2d221f83e6c6434f2da65f79e0ab277520d Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Mon, 6 Feb 2023 13:33:19 +1000 Subject: [PATCH 06/40] types --- .../cypress/support/mock-graphql/clientTestContext.ts | 4 ++-- .../cypress/support/mock-graphql/stubgql-Wizard.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend-shared/cypress/support/mock-graphql/clientTestContext.ts b/packages/frontend-shared/cypress/support/mock-graphql/clientTestContext.ts index 8fc94cb51c2..f0076368fe0 100644 --- a/packages/frontend-shared/cypress/support/mock-graphql/clientTestContext.ts +++ b/packages/frontend-shared/cypress/support/mock-graphql/clientTestContext.ts @@ -3,7 +3,7 @@ import type { CurrentProject, Browser, WizardBundler, - ComponentFrameworkDefinition, + WizardFrontendFramework, GlobalProject, VersionData, LocalSettings, @@ -26,7 +26,7 @@ export interface ClientTestContext { localSettings: LocalSettings wizard: { chosenBundler: WizardBundler | null - chosenFramework: ComponentFrameworkDefinition | null + chosenFramework: WizardFrontendFramework | null chosenManualInstall: boolean allBundlers: WizardBundler[] warnings: [] 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 edb26d327ec..9055eb33b26 100644 --- a/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Wizard.ts +++ b/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Wizard.ts @@ -53,7 +53,7 @@ export const stubWizard: MaybeResolver = { allBundlers, frameworks: testFrameworks.map(({ name, type, supportedBundlers, category, supportStatus }, idx) => { return { - ...testNodeId('ComponentFrameworkDefinition'), + ...testNodeId('WizardFrontendFramework'), supportStatus, name, category, From 9a58381c32421a7edc9375df0980d6a89a1e7044 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Mon, 6 Feb 2023 14:34:51 +1000 Subject: [PATCH 07/40] update code --- packages/graphql/schemas/schema.graphql | 1 + .../schemaTypes/enumTypes/gql-WizardEnums.ts | 4 +- packages/scaffold-config/src/frameworks.ts | 60 ++++++++++++++++++- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/packages/graphql/schemas/schema.graphql b/packages/graphql/schemas/schema.graphql index 39d9435a440..f49974e9b7e 100644 --- a/packages/graphql/schemas/schema.graphql +++ b/packages/graphql/schemas/schema.graphql @@ -2226,6 +2226,7 @@ type Subscription { enum SupportStatusEnum { alpha beta + community full } diff --git a/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts b/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts index 41624f5853f..7b24576adaa 100644 --- a/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts +++ b/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts @@ -1,5 +1,5 @@ import { CODE_LANGUAGES } from '@packages/types' -import { WIZARD_BUNDLERS } from '@packages/scaffold-config' +import { WIZARD_BUNDLERS, supportStatus } from '@packages/scaffold-config' import { enumType } from 'nexus' export const SupportedBundlerEnum = enumType({ @@ -15,7 +15,7 @@ export const WizardConfigFileStatusEnum = enumType({ export const SupportStatusEnum = enumType({ name: 'SupportStatusEnum', - members: ['alpha', 'beta', 'full'], + members: supportStatus, }) export const CodeLanguageEnum = enumType({ diff --git a/packages/scaffold-config/src/frameworks.ts b/packages/scaffold-config/src/frameworks.ts index 209c8fe12d9..2f6eb771a1f 100644 --- a/packages/scaffold-config/src/frameworks.ts +++ b/packages/scaffold-config/src/frameworks.ts @@ -70,6 +70,8 @@ const reactMountModule = async (projectPath: string) => { return semver.major(reactPkg.detectedVersion) === 18 ? 'cypress/react18' : 'cypress/react' } +export const supportStatus = ['alpha', 'beta', 'full', 'community'] as const + export interface ComponentFrameworkDefinition { /** * A semantic, unique identifier. @@ -154,14 +156,14 @@ export interface ComponentFrameworkDefinition { * This is used interally by Cypress for the "Create From Component" feature. * @example 'react' */ - codeGenFramework: 'react' | 'vue' | 'svelte' | 'angular' + codeGenFramework?: 'react' | 'vue' | 'svelte' | 'angular' /** * @internal * This is used interally by Cypress for the "Create From Component" feature. * @example '*.{js,jsx,tsx}' */ - glob: string + glob?: string /** * This is the path to get mount, eg `import { mount } from , @@ -173,7 +175,7 @@ export interface ComponentFrameworkDefinition { * Support status. Internally alpha | beta | full. * Community integrations are "community". */ - supportStatus: 'alpha' | 'beta' | 'full' | 'community' + supportStatus: typeof supportStatus[number] /** * Function returning string for used for the component-index.html file. @@ -393,3 +395,55 @@ export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ componentIndexHtml: componentIndexHtmlGenerator(), }, ] + +const solidDep: CypressComponentDependency = { + type: 'solid', + name: 'Solid.js', + package: 'solid-js', + installer: 'solid-js', + description: 'Solid is a declarative JavaScript library for creating user interfaces', + minVersion: '^1.0.0', +} + +type MaybePromise = T | Promise + +type ThirdPartyComponentFrameworkDefinition = Omit & { + dependencies: (bundler: WizardBundler['type'], projectPath: string) => MaybePromise +} + +export function processThirdPartyFrameworkDefinition (definition: ThirdPartyComponentFrameworkDefinition): ComponentFrameworkDefinition { + return { + ...definition, + supportStatus: 'community', + dependencies: async (bundler, projectPath) => { + // From the third party library - array of dependencies. + const declaredDeps = await definition.dependencies(bundler, projectPath) + const promises = declaredDeps.map((dep) => isDependencyInstalled(dep, projectPath)) + const all = await Promise.all(promises) + + return all + }, + } +} + +// must be default export +export const solidJs: ThirdPartyComponentFrameworkDefinition = { + type: 'solid-js', + + configFramework: 'solid-js', + + category: 'library', + + name: 'Solid.js', + + supportedBundlers: [dependencies.WIZARD_DEPENDENCY_WEBPACK, dependencies.WIZARD_DEPENDENCY_VITE], + + detectors: [solidDep], + + // Cypress will include the bundler dependency here, if they selected one. + dependencies: () => { + return [solidDep] + }, + + mountModule: (projectPath: string) => Promise.resolve('cypress-ct-solid-js'), +} From fe5c399bece9278141243b6d129cef47435fd4fd Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Mon, 6 Feb 2023 14:58:43 +1000 Subject: [PATCH 08/40] use same public API internally --- .../data-context/src/data/coreDataShape.ts | 4 +- packages/scaffold-config/src/frameworks.ts | 135 +++++++++--------- 2 files changed, 67 insertions(+), 72 deletions(-) diff --git a/packages/data-context/src/data/coreDataShape.ts b/packages/data-context/src/data/coreDataShape.ts index e368d4f3e8f..291a4071538 100644 --- a/packages/data-context/src/data/coreDataShape.ts +++ b/packages/data-context/src/data/coreDataShape.ts @@ -1,5 +1,5 @@ import { FoundBrowser, Editor, AllowedState, AllModeOptions, TestingType, BrowserStatus, PACKAGE_MANAGERS, AuthStateName, MIGRATION_STEPS, MigrationStep, BannerState } from '@packages/types' -import { WizardBundler, ComponentFrameworkDefinition, CT_FRAMEWORKS } from '@packages/scaffold-config' +import { WizardBundler, ComponentFrameworkDefinition, CT_FRAMEWORKS, processThirdPartyFrameworkDefinition } from '@packages/scaffold-config' import type { NexusGenObjects } from '@packages/graphql/src/gen/nxs.gen' import type { App, BrowserWindow } from 'electron' import type { ChildProcess } from 'child_process' @@ -198,7 +198,7 @@ export function makeCoreData (modeOptions: Partial = {}): CoreDa chosenManualInstall: false, detectedBundler: null, detectedFramework: null, - frameworks: CT_FRAMEWORKS, + frameworks: CT_FRAMEWORKS.map((framework) => processThirdPartyFrameworkDefinition(framework)), }, migration: { step: 'renameAuto', diff --git a/packages/scaffold-config/src/frameworks.ts b/packages/scaffold-config/src/frameworks.ts index 2f6eb771a1f..e5ad87451d2 100644 --- a/packages/scaffold-config/src/frameworks.ts +++ b/packages/scaffold-config/src/frameworks.ts @@ -50,10 +50,10 @@ export async function isDependencyInstalled (dependency: CypressComponentDepende } } -function getBundlerDependency (bundler: WizardBundler['type'], projectPath: string): Promise { +function getBundler (bundler: WizardBundler['type']) { switch (bundler) { - case 'vite': return isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_VITE, projectPath) - case 'webpack': return isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_WEBPACK, projectPath) + case 'vite': return dependencies.WIZARD_DEPENDENCY_VITE + case 'webpack': return dependencies.WIZARD_DEPENDENCY_WEBPACK default: throw Error(`Unknown bundler ${bundler}`) } } @@ -175,7 +175,7 @@ export interface ComponentFrameworkDefinition { * Support status. Internally alpha | beta | full. * Community integrations are "community". */ - supportStatus: typeof supportStatus[number] + supportStatus?: typeof supportStatus[number] /** * Function returning string for used for the component-index.html file. @@ -189,7 +189,7 @@ export interface ComponentFrameworkDefinition { specPattern?: '**/*.cy.ts' } -export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ +export const CT_FRAMEWORKS: ThirdPartyComponentFrameworkDefinition[] = [ { type: 'reactscripts', configFramework: 'create-react-app', @@ -197,12 +197,12 @@ export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ name: 'Create React App', supportedBundlers: [dependencies.WIZARD_DEPENDENCY_WEBPACK], detectors: [dependencies.WIZARD_DEPENDENCY_REACT_SCRIPTS], - dependencies: (bundler: WizardBundler['type'], projectPath: string): Promise => { - return Promise.all([ - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_REACT_SCRIPTS, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_REACT_DOM, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_REACT, projectPath), - ]) + dependencies: (bundler: WizardBundler['type']): CypressComponentDependency[] => { + return [ + dependencies.WIZARD_DEPENDENCY_REACT_SCRIPTS, + dependencies.WIZARD_DEPENDENCY_REACT_DOM, + dependencies.WIZARD_DEPENDENCY_REACT, + ] }, codeGenFramework: 'react', glob: '*.{js,jsx,tsx}', @@ -217,11 +217,11 @@ export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ name: 'Vue CLI (Vue 2)', detectors: [dependencies.WIZARD_DEPENDENCY_VUE_CLI_SERVICE, dependencies.WIZARD_DEPENDENCY_VUE_2], supportedBundlers: [dependencies.WIZARD_DEPENDENCY_WEBPACK], - dependencies: (bundler: WizardBundler['type'], projectPath: string): Promise => { - return Promise.all([ - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_VUE_CLI_SERVICE, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_VUE_2, projectPath), - ]) + dependencies: (bundler: WizardBundler['type']): CypressComponentDependency[] => { + return [ + dependencies.WIZARD_DEPENDENCY_VUE_CLI_SERVICE, + dependencies.WIZARD_DEPENDENCY_VUE_2, + ] }, codeGenFramework: 'vue', glob: '*.vue', @@ -236,11 +236,11 @@ export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ name: 'Vue CLI (Vue 3)', supportedBundlers: [dependencies.WIZARD_DEPENDENCY_WEBPACK], detectors: [dependencies.WIZARD_DEPENDENCY_VUE_CLI_SERVICE, dependencies.WIZARD_DEPENDENCY_VUE_3], - dependencies: (bundler: WizardBundler['type'], projectPath: string): Promise => { - return Promise.all([ - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_VUE_CLI_SERVICE, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_VUE_3, projectPath), - ]) + dependencies: (bundler: WizardBundler['type']): CypressComponentDependency[] => { + return [ + dependencies.WIZARD_DEPENDENCY_VUE_CLI_SERVICE, + dependencies.WIZARD_DEPENDENCY_VUE_3, + ] }, codeGenFramework: 'vue', glob: '*.vue', @@ -255,12 +255,12 @@ export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ name: 'Next.js', detectors: [dependencies.WIZARD_DEPENDENCY_NEXT], supportedBundlers: [dependencies.WIZARD_DEPENDENCY_WEBPACK], - dependencies: (bundler: WizardBundler['type'], projectPath: string): Promise => { - return Promise.all([ - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_NEXT, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_REACT, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_REACT_DOM, projectPath), - ]) + dependencies: (bundler: WizardBundler['type']): CypressComponentDependency[] => { + return [ + dependencies.WIZARD_DEPENDENCY_NEXT, + dependencies.WIZARD_DEPENDENCY_REACT, + dependencies.WIZARD_DEPENDENCY_REACT_DOM, + ] }, codeGenFramework: 'react', glob: '*.{js,jsx,tsx}', @@ -282,11 +282,11 @@ export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ name: 'Nuxt.js (v2)', detectors: [dependencies.WIZARD_DEPENDENCY_NUXT], supportedBundlers: [dependencies.WIZARD_DEPENDENCY_WEBPACK], - dependencies: (bundler: WizardBundler['type'], projectPath: string): Promise => { - return Promise.all([ - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_NUXT, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_VUE_2, projectPath), - ]) + dependencies: (bundler: WizardBundler['type']): CypressComponentDependency[] => { + return [ + dependencies.WIZARD_DEPENDENCY_NUXT, + dependencies.WIZARD_DEPENDENCY_VUE_2, + ] }, codeGenFramework: 'vue', glob: '*.vue', @@ -301,11 +301,11 @@ export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ name: 'Vue.js 2', detectors: [dependencies.WIZARD_DEPENDENCY_VUE_2], supportedBundlers: [dependencies.WIZARD_DEPENDENCY_WEBPACK, dependencies.WIZARD_DEPENDENCY_VITE], - dependencies: (bundler: WizardBundler['type'], projectPath: string): Promise => { - return Promise.all([ - getBundlerDependency(bundler, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_VUE_2, projectPath), - ]) + dependencies: (bundler: WizardBundler['type']): CypressComponentDependency[] => { + return [ + getBundler(bundler), + dependencies.WIZARD_DEPENDENCY_VUE_2, + ] }, codeGenFramework: 'vue', glob: '*.vue', @@ -320,11 +320,11 @@ export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ name: 'Vue.js 3', detectors: [dependencies.WIZARD_DEPENDENCY_VUE_3], supportedBundlers: [dependencies.WIZARD_DEPENDENCY_WEBPACK, dependencies.WIZARD_DEPENDENCY_VITE], - dependencies: (bundler: WizardBundler['type'], projectPath: string): Promise => { - return Promise.all([ - getBundlerDependency(bundler, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_VUE_3, projectPath), - ]) + dependencies: (bundler: WizardBundler['type']): CypressComponentDependency[] => { + return [ + getBundler(bundler), + dependencies.WIZARD_DEPENDENCY_VUE_3, + ] }, codeGenFramework: 'vue', glob: '*.vue', @@ -339,12 +339,12 @@ export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ name: 'React.js', detectors: [dependencies.WIZARD_DEPENDENCY_REACT], supportedBundlers: [dependencies.WIZARD_DEPENDENCY_WEBPACK, dependencies.WIZARD_DEPENDENCY_VITE], - dependencies: (bundler: WizardBundler['type'], projectPath: string): Promise => { - return Promise.all([ - getBundlerDependency(bundler, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_REACT, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_REACT_DOM, projectPath), - ]) + dependencies: (bundler: WizardBundler['type']): CypressComponentDependency[] => { + return [ + getBundler(bundler), + dependencies.WIZARD_DEPENDENCY_REACT, + dependencies.WIZARD_DEPENDENCY_REACT_DOM, + ] }, codeGenFramework: 'react', glob: '*.{js,jsx,tsx}', @@ -359,14 +359,14 @@ export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ name: 'Angular', detectors: [dependencies.WIZARD_DEPENDENCY_ANGULAR_CLI], supportedBundlers: [dependencies.WIZARD_DEPENDENCY_WEBPACK], - dependencies: (bundler: WizardBundler['type'], projectPath: string): Promise => { - return Promise.all([ - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_ANGULAR_CLI, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_ANGULAR_DEVKIT_BUILD_ANGULAR, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_ANGULAR_CORE, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_ANGULAR_COMMON, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_ANGULAR_PLATFORM_BROWSER_DYNAMIC, projectPath), - ]) + dependencies: (bundler: WizardBundler['type']): CypressComponentDependency[] => { + return [ + dependencies.WIZARD_DEPENDENCY_ANGULAR_CLI, + dependencies.WIZARD_DEPENDENCY_ANGULAR_DEVKIT_BUILD_ANGULAR, + dependencies.WIZARD_DEPENDENCY_ANGULAR_CORE, + dependencies.WIZARD_DEPENDENCY_ANGULAR_COMMON, + dependencies.WIZARD_DEPENDENCY_ANGULAR_PLATFORM_BROWSER_DYNAMIC, + ] }, codeGenFramework: 'angular', glob: '*.component.ts', @@ -382,11 +382,11 @@ export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ name: 'Svelte.js', detectors: [dependencies.WIZARD_DEPENDENCY_SVELTE], supportedBundlers: [dependencies.WIZARD_DEPENDENCY_WEBPACK, dependencies.WIZARD_DEPENDENCY_VITE], - dependencies: (bundler: WizardBundler['type'], projectPath: string): Promise => { - return Promise.all([ - getBundlerDependency(bundler, projectPath), - isDependencyInstalled(dependencies.WIZARD_DEPENDENCY_SVELTE, projectPath), - ]) + dependencies: (bundler: WizardBundler['type']): CypressComponentDependency[] => { + return [ + getBundler(bundler), + dependencies.WIZARD_DEPENDENCY_SVELTE, + ] }, codeGenFramework: 'svelte', glob: '*.svelte', @@ -405,10 +405,8 @@ const solidDep: CypressComponentDependency = { minVersion: '^1.0.0', } -type MaybePromise = T | Promise - -type ThirdPartyComponentFrameworkDefinition = Omit & { - dependencies: (bundler: WizardBundler['type'], projectPath: string) => MaybePromise +type ThirdPartyComponentFrameworkDefinition = Omit & { + dependencies: (bundler: WizardBundler['type']) => CypressComponentDependency[] } export function processThirdPartyFrameworkDefinition (definition: ThirdPartyComponentFrameworkDefinition): ComponentFrameworkDefinition { @@ -416,12 +414,9 @@ export function processThirdPartyFrameworkDefinition (definition: ThirdPartyComp ...definition, supportStatus: 'community', dependencies: async (bundler, projectPath) => { - // From the third party library - array of dependencies. - const declaredDeps = await definition.dependencies(bundler, projectPath) - const promises = declaredDeps.map((dep) => isDependencyInstalled(dep, projectPath)) - const all = await Promise.all(promises) + const declaredDeps = definition.dependencies(bundler) - return all + return await Promise.all(declaredDeps.map((dep) => isDependencyInstalled(dep, projectPath))) }, } } From c9dc13bf2927d0db3a799d5f5f75d13a48dc03af Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Tue, 7 Feb 2023 09:52:14 +1000 Subject: [PATCH 09/40] rename interfaces --- packages/data-context/src/actions/CodegenActions.ts | 4 ++-- packages/data-context/src/actions/WizardActions.ts | 6 +++--- packages/data-context/src/codegen/spec-options.ts | 4 ++-- .../data-context/src/data/ProjectConfigManager.ts | 4 ++-- packages/data-context/src/data/coreDataShape.ts | 8 ++++---- .../test/unit/sources/WizardDataSource.spec.ts | 4 ++-- .../objectTypes/gql-WizardFrontendFramework.ts | 2 +- .../launchpad/cypress/e2e/scaffold-project.cy.ts | 6 +++--- packages/scaffold-config/src/detect.ts | 8 ++++---- packages/scaffold-config/src/frameworks.ts | 12 ++++++------ 10 files changed, 29 insertions(+), 29 deletions(-) diff --git a/packages/data-context/src/actions/CodegenActions.ts b/packages/data-context/src/actions/CodegenActions.ts index 52c41ee0c4c..bc05f268630 100644 --- a/packages/data-context/src/actions/CodegenActions.ts +++ b/packages/data-context/src/actions/CodegenActions.ts @@ -5,7 +5,7 @@ import type { DataContext } from '..' import { SpecOptions, codeGenerator } from '../codegen' import templates from '../codegen/templates' import type { CodeGenType } from '../gen/graphcache-config.gen' -import type { ComponentFrameworkDefinition } from '@packages/scaffold-config' +import type { ResolvedComponentFrameworkDefinition } from '@packages/scaffold-config' import { parse as parseReactComponent, resolver as reactDocgenResolvers } from 'react-docgen' import { visit } from 'ast-types' @@ -152,7 +152,7 @@ export class CodegenActions { }) } - getWizardFrameworkFromConfig (): ComponentFrameworkDefinition | undefined { + getWizardFrameworkFromConfig (): ResolvedComponentFrameworkDefinition | undefined { const config = this.ctx.lifecycleManager.loadedConfigFile // If devServer is a function, they are using a custom dev server. diff --git a/packages/data-context/src/actions/WizardActions.ts b/packages/data-context/src/actions/WizardActions.ts index 21afd26e11e..bbaaf4754cd 100644 --- a/packages/data-context/src/actions/WizardActions.ts +++ b/packages/data-context/src/actions/WizardActions.ts @@ -1,5 +1,5 @@ import type { NexusGenObjects } from '@packages/graphql/src/gen/nxs.gen' -import { detectFramework, commandsFileBody, supportFileComponent, supportFileE2E, WizardBundler, ComponentFrameworkDefinition } from '@packages/scaffold-config' +import { detectFramework, commandsFileBody, supportFileComponent, supportFileE2E, WizardBundler, ResolvedComponentFrameworkDefinition } from '@packages/scaffold-config' import assert from 'assert' import path from 'path' import Debug from 'debug' @@ -24,7 +24,7 @@ export class WizardActions { return this.ctx.wizardData } - setFramework (framework: ComponentFrameworkDefinition | null): void { + setFramework (framework: ResolvedComponentFrameworkDefinition | null): void { const next = this.ctx.coreData.wizard.frameworks.find((x) => x.type === framework?.type) this.ctx.update((coreData) => { @@ -296,7 +296,7 @@ export class WizardActions { } } - private async scaffoldComponentIndexHtml (chosenFramework: ComponentFrameworkDefinition): Promise { + private async scaffoldComponentIndexHtml (chosenFramework: ResolvedComponentFrameworkDefinition): Promise { const componentIndexHtmlPath = path.join(this.projectRoot, 'cypress', 'support', 'component-index.html') await this.ensureDir('support') diff --git a/packages/data-context/src/codegen/spec-options.ts b/packages/data-context/src/codegen/spec-options.ts index ddde2eef181..45e093bd639 100644 --- a/packages/data-context/src/codegen/spec-options.ts +++ b/packages/data-context/src/codegen/spec-options.ts @@ -1,6 +1,6 @@ import type { ParsedPath } from 'path' import type { CodeGenType } from '@packages/graphql/src/gen/nxs.gen' -import type { ComponentFrameworkDefinition } from '@packages/scaffold-config' +import type { ResolvedComponentFrameworkDefinition } from '@packages/scaffold-config' import fs from 'fs-extra' import { uniq, upperFirst } from 'lodash' import path from 'path' @@ -14,7 +14,7 @@ interface CodeGenOptions { isDefaultSpecPattern: boolean specPattern: string[] currentProject: string | null - framework?: ComponentFrameworkDefinition + framework?: ResolvedComponentFrameworkDefinition specs?: FoundSpec[] componentName?: string isDefault?: boolean diff --git a/packages/data-context/src/data/ProjectConfigManager.ts b/packages/data-context/src/data/ProjectConfigManager.ts index 78f8b0485f1..8d03be52640 100644 --- a/packages/data-context/src/data/ProjectConfigManager.ts +++ b/packages/data-context/src/data/ProjectConfigManager.ts @@ -18,7 +18,7 @@ import { CypressEnv } from './CypressEnv' import { autoBindDebug } from '../util/autoBindDebug' import type { EventRegistrar } from './EventRegistrar' import type { DataContext } from '../DataContext' -import { DependencyToInstall, isDependencyInstalled, WIZARD_BUNDLERS, ComponentFrameworkDefinition } from '@packages/scaffold-config' +import { DependencyToInstall, isDependencyInstalled, WIZARD_BUNDLERS, ResolvedComponentFrameworkDefinition } from '@packages/scaffold-config' const debug = debugLib(`cypress:lifecycle:ProjectConfigManager`) @@ -197,7 +197,7 @@ export class ProjectConfigManager { return } - const isFrameworkSatisfied = async (bundler: typeof WIZARD_BUNDLERS[number], framework: ComponentFrameworkDefinition) => { + const isFrameworkSatisfied = async (bundler: typeof WIZARD_BUNDLERS[number], framework: ResolvedComponentFrameworkDefinition) => { for (const dep of await (framework.dependencies(bundler.type, this.options.projectRoot))) { const res = await isDependencyInstalled(dep.dependency, this.options.projectRoot) diff --git a/packages/data-context/src/data/coreDataShape.ts b/packages/data-context/src/data/coreDataShape.ts index 291a4071538..55ca4b1713b 100644 --- a/packages/data-context/src/data/coreDataShape.ts +++ b/packages/data-context/src/data/coreDataShape.ts @@ -1,5 +1,5 @@ import { FoundBrowser, Editor, AllowedState, AllModeOptions, TestingType, BrowserStatus, PACKAGE_MANAGERS, AuthStateName, MIGRATION_STEPS, MigrationStep, BannerState } from '@packages/types' -import { WizardBundler, ComponentFrameworkDefinition, CT_FRAMEWORKS, processThirdPartyFrameworkDefinition } from '@packages/scaffold-config' +import { WizardBundler, ResolvedComponentFrameworkDefinition, CT_FRAMEWORKS, processThirdPartyFrameworkDefinition } from '@packages/scaffold-config' import type { NexusGenObjects } from '@packages/graphql/src/gen/nxs.gen' import type { App, BrowserWindow } from 'electron' import type { ChildProcess } from 'child_process' @@ -66,11 +66,11 @@ export interface AppDataShape { export interface WizardDataShape { chosenBundler: WizardBundler | null - chosenFramework: ComponentFrameworkDefinition | null + chosenFramework: ResolvedComponentFrameworkDefinition | null chosenManualInstall: boolean detectedBundler: WizardBundler | null - detectedFramework: ComponentFrameworkDefinition | null - frameworks: ComponentFrameworkDefinition[] + detectedFramework: ResolvedComponentFrameworkDefinition | null + frameworks: ResolvedComponentFrameworkDefinition[] } export interface MigrationDataShape { diff --git a/packages/data-context/test/unit/sources/WizardDataSource.spec.ts b/packages/data-context/test/unit/sources/WizardDataSource.spec.ts index 8b4ffad643a..31b011f28a2 100644 --- a/packages/data-context/test/unit/sources/WizardDataSource.spec.ts +++ b/packages/data-context/test/unit/sources/WizardDataSource.spec.ts @@ -1,8 +1,8 @@ -import { WizardBundler, ComponentFrameworkDefinition, WIZARD_BUNDLERS, CT_FRAMEWORKS } from '@packages/scaffold-config' +import { WizardBundler, ResolvedComponentFrameworkDefinition, WIZARD_BUNDLERS, CT_FRAMEWORKS } from '@packages/scaffold-config' import { expect } from 'chai' import { createTestDataContext, scaffoldMigrationProject, removeCommonNodeModules } from '../helper' -function findFramework (type: ComponentFrameworkDefinition['type']) { +function findFramework (type: ResolvedComponentFrameworkDefinition['type']) { return CT_FRAMEWORKS.find((x) => x.type === type)! } diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts index ae0dc8601a4..c1b31df4061 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts @@ -46,6 +46,6 @@ export const WizardFrontendFramework = objectType({ sourceType: { module: '@packages/scaffold-config', - export: 'ComponentFrameworkDefinition', + export: 'ResolvedComponentFrameworkDefinition', }, }) diff --git a/packages/launchpad/cypress/e2e/scaffold-project.cy.ts b/packages/launchpad/cypress/e2e/scaffold-project.cy.ts index 79386b9beed..e6bc30c7fdd 100644 --- a/packages/launchpad/cypress/e2e/scaffold-project.cy.ts +++ b/packages/launchpad/cypress/e2e/scaffold-project.cy.ts @@ -1,4 +1,4 @@ -import type { ComponentFrameworkDefinition } from '@packages/scaffold-config' +import type { ResolvedComponentFrameworkDefinition } from '@packages/scaffold-config' import type { SnapshotScaffoldTestResult } from '@packages/launchpad/cypress/tasks/snapshotsScaffold' // The tests in this file take an existing project without Cypress Configured @@ -56,8 +56,8 @@ function scaffoldAndOpenE2EProject (opts: { function scaffoldAndOpenCTProject (opts: { name: Parameters[0] - framework: ComponentFrameworkDefinition['name'] - bundler?: ComponentFrameworkDefinition['supportedBundlers'][number]['name'] + framework: ResolvedComponentFrameworkDefinition['name'] + bundler?: ResolvedComponentFrameworkDefinition['supportedBundlers'][number]['name'] args?: Parameters[1] removeFixturesFolder?: boolean }) { diff --git a/packages/scaffold-config/src/detect.ts b/packages/scaffold-config/src/detect.ts index a7ce7b82518..8e2881409a0 100644 --- a/packages/scaffold-config/src/detect.ts +++ b/packages/scaffold-config/src/detect.ts @@ -1,4 +1,4 @@ -import { isDependencyInstalled, WizardBundler, ComponentFrameworkDefinition } from './frameworks' +import { isDependencyInstalled, WizardBundler, ResolvedComponentFrameworkDefinition } from './frameworks' import { WIZARD_BUNDLERS } from './dependencies' import path from 'path' import fs from 'fs' @@ -9,11 +9,11 @@ import Debug from 'debug' const debug = Debug('cypress:scaffold-config:detect') interface DetectFramework { - framework?: ComponentFrameworkDefinition + framework?: ResolvedComponentFrameworkDefinition bundler?: WizardBundler } -export async function areAllDepsSatisified (projectPath: string, framework: ComponentFrameworkDefinition) { +export async function areAllDepsSatisified (projectPath: string, framework: ResolvedComponentFrameworkDefinition) { for (const dep of framework.detectors) { const result = await isDependencyInstalled(dep, projectPath) @@ -32,7 +32,7 @@ export async function areAllDepsSatisified (projectPath: string, framework: Comp // If we don't find a specific framework, but we do find a library and/or // bundler, we return both the framework, which might just be "React", // and the bundler, which could be Vite. -export async function detectFramework (projectPath: string, frameworks: ComponentFrameworkDefinition[]): Promise { +export async function detectFramework (projectPath: string, frameworks: ResolvedComponentFrameworkDefinition[]): Promise { // first see if it's a template for (const framework of frameworks.filter((x) => x.category === 'template')) { const hasAllDeps = await areAllDepsSatisified(projectPath, framework) diff --git a/packages/scaffold-config/src/frameworks.ts b/packages/scaffold-config/src/frameworks.ts index e5ad87451d2..a5286006e2b 100644 --- a/packages/scaffold-config/src/frameworks.ts +++ b/packages/scaffold-config/src/frameworks.ts @@ -9,7 +9,7 @@ export type PkgJson = { version: string, dependencies?: Record, export type WizardBundler = typeof dependencies.WIZARD_BUNDLERS[number] -export type CodeGenFramework = ComponentFrameworkDefinition['codeGenFramework'] +export type CodeGenFramework = ResolvedComponentFrameworkDefinition['codeGenFramework'] export interface DependencyToInstall { dependency: CypressComponentDependency @@ -72,7 +72,7 @@ const reactMountModule = async (projectPath: string) => { export const supportStatus = ['alpha', 'beta', 'full', 'community'] as const -export interface ComponentFrameworkDefinition { +export interface ResolvedComponentFrameworkDefinition { /** * A semantic, unique identifier. * Example: 'reactscripts', 'nextjs' @@ -189,7 +189,7 @@ export interface ComponentFrameworkDefinition { specPattern?: '**/*.cy.ts' } -export const CT_FRAMEWORKS: ThirdPartyComponentFrameworkDefinition[] = [ +export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ { type: 'reactscripts', configFramework: 'create-react-app', @@ -405,11 +405,11 @@ const solidDep: CypressComponentDependency = { minVersion: '^1.0.0', } -type ThirdPartyComponentFrameworkDefinition = Omit & { +type ComponentFrameworkDefinition = Omit & { dependencies: (bundler: WizardBundler['type']) => CypressComponentDependency[] } -export function processThirdPartyFrameworkDefinition (definition: ThirdPartyComponentFrameworkDefinition): ComponentFrameworkDefinition { +export function processThirdPartyFrameworkDefinition (definition: ComponentFrameworkDefinition): ResolvedComponentFrameworkDefinition { return { ...definition, supportStatus: 'community', @@ -422,7 +422,7 @@ export function processThirdPartyFrameworkDefinition (definition: ThirdPartyComp } // must be default export -export const solidJs: ThirdPartyComponentFrameworkDefinition = { +export const solidJs: ComponentFrameworkDefinition = { type: 'solid-js', configFramework: 'solid-js', From 57a8c33abc5482da8cbedab999b525b7be866391 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Tue, 7 Feb 2023 09:54:48 +1000 Subject: [PATCH 10/40] rename --- packages/data-context/src/data/coreDataShape.ts | 4 ++-- packages/scaffold-config/src/frameworks.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/data-context/src/data/coreDataShape.ts b/packages/data-context/src/data/coreDataShape.ts index 55ca4b1713b..f3c2283d03b 100644 --- a/packages/data-context/src/data/coreDataShape.ts +++ b/packages/data-context/src/data/coreDataShape.ts @@ -1,5 +1,5 @@ import { FoundBrowser, Editor, AllowedState, AllModeOptions, TestingType, BrowserStatus, PACKAGE_MANAGERS, AuthStateName, MIGRATION_STEPS, MigrationStep, BannerState } from '@packages/types' -import { WizardBundler, ResolvedComponentFrameworkDefinition, CT_FRAMEWORKS, processThirdPartyFrameworkDefinition } from '@packages/scaffold-config' +import { WizardBundler, ResolvedComponentFrameworkDefinition, CT_FRAMEWORKS, resolveComponentFrameworkDefinition } from '@packages/scaffold-config' import type { NexusGenObjects } from '@packages/graphql/src/gen/nxs.gen' import type { App, BrowserWindow } from 'electron' import type { ChildProcess } from 'child_process' @@ -198,7 +198,7 @@ export function makeCoreData (modeOptions: Partial = {}): CoreDa chosenManualInstall: false, detectedBundler: null, detectedFramework: null, - frameworks: CT_FRAMEWORKS.map((framework) => processThirdPartyFrameworkDefinition(framework)), + frameworks: CT_FRAMEWORKS.map((framework) => resolveComponentFrameworkDefinition(framework)), }, migration: { step: 'renameAuto', diff --git a/packages/scaffold-config/src/frameworks.ts b/packages/scaffold-config/src/frameworks.ts index a5286006e2b..3bdbd92b737 100644 --- a/packages/scaffold-config/src/frameworks.ts +++ b/packages/scaffold-config/src/frameworks.ts @@ -409,7 +409,7 @@ type ComponentFrameworkDefinition = Omit CypressComponentDependency[] } -export function processThirdPartyFrameworkDefinition (definition: ComponentFrameworkDefinition): ResolvedComponentFrameworkDefinition { +export function resolveComponentFrameworkDefinition (definition: ComponentFrameworkDefinition): ResolvedComponentFrameworkDefinition { return { ...definition, supportStatus: 'community', From 9582e8d3bb4b44fe95b66f91105e23fb8ee0a53c Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Tue, 7 Feb 2023 11:40:08 +1000 Subject: [PATCH 11/40] work on dev server api --- npm/webpack-dev-server/src/devServer.ts | 13 ++++++- .../src/data/ProjectConfigManager.ts | 7 +++- .../data-context/src/data/coreDataShape.ts | 4 +- packages/scaffold-config/src/frameworks.ts | 37 ++++++++++++++++--- 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/npm/webpack-dev-server/src/devServer.ts b/npm/webpack-dev-server/src/devServer.ts index e7cabad50c1..f1db2d74178 100644 --- a/npm/webpack-dev-server/src/devServer.ts +++ b/npm/webpack-dev-server/src/devServer.ts @@ -115,6 +115,17 @@ export type PresetHandlerResult = { frameworkConfig: Configuration, sourceWebpac type Optional = Pick, K> & Omit async function getPreset (devServerConfig: WebpackDevServerConfig): Promise> { + const defaultWebpackModules = () => ({ sourceWebpackModulesResult: sourceDefaultWebpackDependencies(devServerConfig) }) + + // Third party framework + if (devServerConfig.framework?.startsWith('cypress-ct-')) { + return { + ...defaultWebpackModules(), + // Call their config handler, if they provided one. + // frameworkConfig: devServerConfig.getDevServerConfig?.(devServerConfig.cypressConfig.projectRoot) + } + } + switch (devServerConfig.framework) { case 'create-react-app': return createReactAppHandler(devServerConfig) @@ -134,7 +145,7 @@ async function getPreset (devServerConfig: WebpackDevServerConfig): Promise { - for (const dep of await (framework.dependencies(bundler.type, this.options.projectRoot))) { + const deps = await (framework.dependencies(bundler.type, this.options.projectRoot)) + + debug('deps are %o', deps) + + for (const dep of deps) { + debug('detecting %s in %s', dep.dependency.name, this.options.projectRoot) const res = await isDependencyInstalled(dep.dependency, this.options.projectRoot) if (!res.satisfied) { diff --git a/packages/data-context/src/data/coreDataShape.ts b/packages/data-context/src/data/coreDataShape.ts index f3c2283d03b..42cb12eabab 100644 --- a/packages/data-context/src/data/coreDataShape.ts +++ b/packages/data-context/src/data/coreDataShape.ts @@ -1,5 +1,5 @@ import { FoundBrowser, Editor, AllowedState, AllModeOptions, TestingType, BrowserStatus, PACKAGE_MANAGERS, AuthStateName, MIGRATION_STEPS, MigrationStep, BannerState } from '@packages/types' -import { WizardBundler, ResolvedComponentFrameworkDefinition, CT_FRAMEWORKS, resolveComponentFrameworkDefinition } from '@packages/scaffold-config' +import { WizardBundler, ResolvedComponentFrameworkDefinition, CT_FRAMEWORKS, resolveComponentFrameworkDefinition, solidJs } from '@packages/scaffold-config' import type { NexusGenObjects } from '@packages/graphql/src/gen/nxs.gen' import type { App, BrowserWindow } from 'electron' import type { ChildProcess } from 'child_process' @@ -198,7 +198,7 @@ export function makeCoreData (modeOptions: Partial = {}): CoreDa chosenManualInstall: false, detectedBundler: null, detectedFramework: null, - frameworks: CT_FRAMEWORKS.map((framework) => resolveComponentFrameworkDefinition(framework)), + frameworks: [...CT_FRAMEWORKS, solidJs].map((framework) => resolveComponentFrameworkDefinition(framework)), }, migration: { step: 'renameAuto', diff --git a/packages/scaffold-config/src/frameworks.ts b/packages/scaffold-config/src/frameworks.ts index 3bdbd92b737..ae0458a5c0b 100644 --- a/packages/scaffold-config/src/frameworks.ts +++ b/packages/scaffold-config/src/frameworks.ts @@ -3,14 +3,19 @@ import fs from 'fs-extra' import * as dependencies from './dependencies' import componentIndexHtmlGenerator from './component-index-template' import type { CypressComponentDependency } from './dependencies' +import debugLib from 'debug' import semver from 'semver' +const debug = debugLib('cypress:scaffold-config:frameworks') + export type PkgJson = { version: string, dependencies?: Record, devDependencies?: Record } export type WizardBundler = typeof dependencies.WIZARD_BUNDLERS[number] export type CodeGenFramework = ResolvedComponentFrameworkDefinition['codeGenFramework'] +type MaybePromise = Promise | T + export interface DependencyToInstall { dependency: CypressComponentDependency satisfied: boolean @@ -20,12 +25,15 @@ export interface DependencyToInstall { export async function isDependencyInstalled (dependency: CypressComponentDependency, projectPath: string): Promise { try { + debug('detecting %s in %s', dependency.package, projectPath) const loc = require.resolve(path.join(dependency.package, 'package.json'), { paths: [projectPath], }) const pkg = await fs.readJson(loc) as PkgJson + debug('found package.json %o', pkg) + if (!pkg.version) { throw Error(`${pkg.version} for ${dependency.package} is not a valid semantic version.`) } @@ -34,6 +42,8 @@ export async function isDependencyInstalled (dependency: CypressComponentDepende includePrerelease: true, })) + debug('%s is satisfied? %s', dependency.package, satisfied) + return { dependency, detectedVersion: pkg.version, @@ -41,6 +51,8 @@ export async function isDependencyInstalled (dependency: CypressComponentDepende satisfied, } } catch (e) { + debug('error when detecting %s: %s', dependency.package, e.message) + return { dependency, detectedVersion: null, @@ -50,7 +62,7 @@ export async function isDependencyInstalled (dependency: CypressComponentDepende } } -function getBundler (bundler: WizardBundler['type']) { +function getBundler (bundler: WizardBundler['type']): CypressComponentDependency { switch (bundler) { case 'vite': return dependencies.WIZARD_DEPENDENCY_VITE case 'webpack': return dependencies.WIZARD_DEPENDENCY_WEBPACK @@ -116,7 +128,7 @@ export interface ResolvedComponentFrameworkDefinition { * * NOTE: This could be a "fast follow" if we want to reduce the scope of this brief. */ - getDevServerConfig?: () => Promise<{ frameworkConfig: any }> + getDevServerConfig?: (projectPath: string) => MaybePromise /** * Name displayed in Launchpad when doing initial setup. @@ -397,7 +409,7 @@ export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ ] const solidDep: CypressComponentDependency = { - type: 'solid', + type: 'solid-js', name: 'Solid.js', package: 'solid-js', installer: 'solid-js', @@ -411,11 +423,16 @@ type ComponentFrameworkDefinition = Omit { const declaredDeps = definition.dependencies(bundler) + // Must add bundler based on launchpad selection if it's a third party definition. + if (definition.type.startsWith('cypress-ct-')) { + declaredDeps.push(getBundler(bundler)) + } + return await Promise.all(declaredDeps.map((dep) => isDependencyInstalled(dep, projectPath))) }, } @@ -423,9 +440,9 @@ export function resolveComponentFrameworkDefinition (definition: ComponentFramew // must be default export export const solidJs: ComponentFrameworkDefinition = { - type: 'solid-js', + type: 'cypress-ct-solid-js', - configFramework: 'solid-js', + configFramework: 'cypress-ct-solid-js', category: 'library', @@ -433,6 +450,14 @@ export const solidJs: ComponentFrameworkDefinition = { supportedBundlers: [dependencies.WIZARD_DEPENDENCY_WEBPACK, dependencies.WIZARD_DEPENDENCY_VITE], + getDevServerConfig: (projectRoot) => { + // console.log('running getDevServerConfig', projectRoot) + const c = require(require.resolve('webpack.config.js', { paths: [projectRoot] })) + + // console.log(c) + return c + }, + detectors: [solidDep], // Cypress will include the bundler dependency here, if they selected one. From aa03f2278bc9cdb3812c11b3fd136551ca510121 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Tue, 7 Feb 2023 11:42:59 +1000 Subject: [PATCH 12/40] fix types --- packages/launchpad/package.json | 1 + packages/launchpad/src/setup/SelectFwOrBundler.vue | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/launchpad/package.json b/packages/launchpad/package.json index 72aba3e73b7..76e27db14a4 100644 --- a/packages/launchpad/package.json +++ b/packages/launchpad/package.json @@ -28,6 +28,7 @@ "@iconify/vue": "3.0.0-beta.1", "@intlify/vite-plugin-vue-i18n": "2.4.0", "@packages/frontend-shared": "0.0.0-development", + "@packages/scaffold-config": "0.0.0-development", "@percy/cypress": "^3.1.0", "@purge-icons/generated": "0.8.1", "@testing-library/cypress": "9.0.0", diff --git a/packages/launchpad/src/setup/SelectFwOrBundler.vue b/packages/launchpad/src/setup/SelectFwOrBundler.vue index 682d9dc62b1..2801847d990 100644 --- a/packages/launchpad/src/setup/SelectFwOrBundler.vue +++ b/packages/launchpad/src/setup/SelectFwOrBundler.vue @@ -64,6 +64,8 @@ - diff --git a/packages/launchpad/src/setup/SelectFwOrBundler.cy.tsx b/packages/launchpad/src/setup/SelectFwOrBundler.cy.tsx index 6b9be1c23e2..ed9de47457c 100644 --- a/packages/launchpad/src/setup/SelectFwOrBundler.cy.tsx +++ b/packages/launchpad/src/setup/SelectFwOrBundler.cy.tsx @@ -72,6 +72,26 @@ describe('', () => { cy.contains('button', 'placeholder').should('exist') }) + it('shows a community integration', () => { + cy.mount(() => ( + + )) + + cy.percySnapshot() + }) + it('should select the value', () => { cy.mount(() => ( diff --git a/packages/launchpad/src/setup/SelectFwOrBundler.vue b/packages/launchpad/src/setup/SelectFwOrBundler.vue index 1b181e9ec00..caf3f864ccd 100644 --- a/packages/launchpad/src/setup/SelectFwOrBundler.vue +++ b/packages/launchpad/src/setup/SelectFwOrBundler.vue @@ -34,6 +34,7 @@ {{ selectedOptionName }} + + Date: Fri, 10 Feb 2023 14:15:58 +1000 Subject: [PATCH 27/40] refactor --- cli/index.mjs | 2 + cli/lib/cypress.js | 18 ++++++ cli/types/cypress-npm-api.d.ts | 16 +++++ cli/types/cypress.d.ts | 7 +++ packages/scaffold-config/src/frameworks.ts | 58 +++++++++---------- .../scaffold-config/test/unit/detect.spec.ts | 2 +- .../scaffold-config/test/unit/fixtures.ts | 8 +-- 7 files changed, 73 insertions(+), 38 deletions(-) diff --git a/cli/index.mjs b/cli/index.mjs index 7b616f65a34..dcf09178f39 100644 --- a/cli/index.mjs +++ b/cli/index.mjs @@ -8,6 +8,8 @@ export default cypress export const defineConfig = cypress.defineConfig +export const defineComponentFramework = cypress.defineComponentFramework + export const run = cypress.run export const open = cypress.open diff --git a/cli/lib/cypress.js b/cli/lib/cypress.js index 02348266aa5..376aa7c03b2 100644 --- a/cli/lib/cypress.js +++ b/cli/lib/cypress.js @@ -87,6 +87,24 @@ const cypressModuleApi = { defineConfig (config) { return config }, + + /** + * Provides automatic code completion for Component Frameworks Definitions. + * While it's not strictly necessary for Cypress to parse your configuration, we + * recommend wrapping your Component Framework Definition object with `defineComponentFrameworkDefinition()` + * @example + * module.exports = defineComponentFrameworkDefinition({ + * type: 'cypress-ct-solid-js' + * // ... + * }) + * + * @see ../types/cypress-npm-api.d.ts + * @param {Cypress.ComponentFrameworkDefinition} config + * @returns {Cypress.ComponentFrameworkDefinition} the configuration passed in parameter + */ + defineComponentFrameworkDefinition (config) { + return config + }, } module.exports = cypressModuleApi diff --git a/cli/types/cypress-npm-api.d.ts b/cli/types/cypress-npm-api.d.ts index a7051b7f146..1066cb9ba89 100644 --- a/cli/types/cypress-npm-api.d.ts +++ b/cli/types/cypress-npm-api.d.ts @@ -393,6 +393,22 @@ declare module 'cypress' { * @returns {Cypress.ConfigOptions} the configuration passed in parameter */ defineConfig(config: Cypress.ConfigOptions): Cypress.ConfigOptions + + /** + * Provides automatic code completion for Component Frameworks Definitions. + * While it's not strictly necessary for Cypress to parse your configuration, we + * recommend wrapping your Component Framework Definition object with `defineComponentFrameworkDefinition()` + * @example + * module.exports = defineComponentFrameworkDefinition({ + * type: 'cypress-ct-solid-js' + * // ... + * }) + * + * @see ../types/cypress-npm-api.d.ts + * @param {Cypress.ThirdPartyComponentFrameworkDefinition} config + * @returns {Cypress.ThirdPartyComponentFrameworkDefinition} the configuration passed in parameter + */ + defineComponentFrameworkDefinition (config: Cypress.ThirdPartyComponentFrameworkDefinition): Cypress.ThirdPartyComponentFrameworkDefinition } // export Cypress NPM module interface diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 7ea20d91720..02c3c6d0210 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -3400,6 +3400,13 @@ declare namespace Cypress { specPattern?: '**/*.cy.ts' } + type ComponentFrameworkDefinition = Omit & { + dependencies: (bundler: 'webpack' | 'vite') => Cypress.CypressComponentDependency[] + } + + // Certain properties are not supported for third party frameworks right now, such as ones related to the "Create From" feature. + type ThirdPartyComponentFrameworkDefinition = Omit + interface AngularDevServerProjectConfig { root: string sourceRoot: string diff --git a/packages/scaffold-config/src/frameworks.ts b/packages/scaffold-config/src/frameworks.ts index f40cd0d8249..60385f31f6d 100644 --- a/packages/scaffold-config/src/frameworks.ts +++ b/packages/scaffold-config/src/frameworks.ts @@ -74,7 +74,7 @@ const reactMountModule = async (projectPath: string) => { export const supportStatus = ['alpha', 'beta', 'full', 'community'] as const -export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ +export const CT_FRAMEWORKS: Cypress.ComponentFrameworkDefinition[] = [ { type: 'reactscripts', configFramework: 'create-react-app', @@ -281,40 +281,36 @@ export const CT_FRAMEWORKS: ComponentFrameworkDefinition[] = [ }, ] -export type ComponentFrameworkDefinition = Omit & { - dependencies: (bundler: WizardBundler['type']) => Cypress.CypressComponentDependency[] -} - -export function defineComponentFrameworkInternal (definition: ComponentFrameworkDefinition): ComponentFrameworkDefinition { - return definition -} - -// Certain properties are not supported for third party frameworks right now, such as ones related to the "Create From" feature. -type ThirdPartyComponentFrameworkDefinition = Omit - /** - * Define a component framework to be embedded in the Cypress Component Testing - * onboarding workflow. - * - * This is a no-op at runtime - it's purely for type safety. + * Given a first or third party Component Framework Definition, + * resolves into a unified ResolvedComponentFrameworkDefinition. + * This way we have a single type used throughout Cypress. */ -export function defineComponentFramework (definition: ThirdPartyComponentFrameworkDefinition): ThirdPartyComponentFrameworkDefinition { - return definition -} +export function resolveComponentFrameworkDefinition (definition: Cypress.ComponentFrameworkDefinition | Cypress.ThirdPartyComponentFrameworkDefinition): Cypress.ResolvedComponentFrameworkDefinition { + const isThirdPartyDefinition = definition.type.startsWith('cypress-ct-') -export function resolveComponentFrameworkDefinition (definition: ComponentFrameworkDefinition | ThirdPartyComponentFrameworkDefinition): Cypress.ResolvedComponentFrameworkDefinition { - return { - ...definition, - supportStatus: definition.type.startsWith('cypress-ct-') ? 'community' : (definition as ComponentFrameworkDefinition).supportStatus, - dependencies: async (bundler, projectPath) => { - const declaredDeps = definition.dependencies(bundler) + const dependencies: Cypress.ResolvedComponentFrameworkDefinition['dependencies'] = async (bundler, projectPath) => { + const declaredDeps = definition.dependencies(bundler) - // Must add bundler based on launchpad selection if it's a third party definition. - if (definition.type.startsWith('cypress-ct-')) { - declaredDeps.push(getBundler(bundler)) - } + // Must add bundler based on launchpad selection if it's a third party definition. + declaredDeps.push(getBundler(bundler)) - return await Promise.all(declaredDeps.map((dep) => isDependencyInstalled(dep, projectPath))) - }, + return await Promise.all(declaredDeps.map((dep) => isDependencyInstalled(dep, projectPath))) } + + if (isThirdPartyDefinition) { + const def = definition as Cypress.ThirdPartyComponentFrameworkDefinition + + return { + ...def, + dependencies, + configFramework: def.type, + supportStatus: 'community', + mountModule: () => Promise.resolve(definition.type), + } + } + + const def = definition as Cypress.ComponentFrameworkDefinition + + return { ...def, dependencies } } diff --git a/packages/scaffold-config/test/unit/detect.spec.ts b/packages/scaffold-config/test/unit/detect.spec.ts index fe52a421b91..b841ce9d4f4 100644 --- a/packages/scaffold-config/test/unit/detect.spec.ts +++ b/packages/scaffold-config/test/unit/detect.spec.ts @@ -6,7 +6,7 @@ import type { ProjectFixtureDir } from '@tooling/system-tests' import { detectFramework, detectLanguage, PkgJson, CT_FRAMEWORKS, resolveComponentFrameworkDefinition, WIZARD_DEPENDENCY_WEBPACK } from '../../src' import Fixtures from '@tooling/system-tests' import path from 'path' -import { solidJs, solidDep } from './fixtures' +import solidJs, { solidDep } from './fixtures' beforeEach(() => { // @ts-ignore diff --git a/packages/scaffold-config/test/unit/fixtures.ts b/packages/scaffold-config/test/unit/fixtures.ts index 8373ef381f0..2cdf9400a40 100644 --- a/packages/scaffold-config/test/unit/fixtures.ts +++ b/packages/scaffold-config/test/unit/fixtures.ts @@ -1,4 +1,4 @@ -import { defineComponentFramework } from '../../src' +import { defineComponentFrameworkDefinition } from 'cypress' export const solidDep: Cypress.CypressComponentDependency = { type: 'solid-js', @@ -10,11 +10,9 @@ export const solidDep: Cypress.CypressComponentDependency = { } // must be default export -export const solidJs = defineComponentFramework({ +export default defineComponentFrameworkDefinition({ type: 'cypress-ct-solid-js', - configFramework: 'cypress-ct-solid-js', - category: 'library', name: 'Solid.js', @@ -27,6 +25,4 @@ export const solidJs = defineComponentFramework({ dependencies: () => { return [solidDep] }, - - mountModule: (projectPath: string) => Promise.resolve('cypress-ct-solid-js'), }) From c8087c174744b9beaa5b36f94ed55cb47ab62e0f Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Fri, 10 Feb 2023 14:48:53 +1000 Subject: [PATCH 28/40] types --- cli/lib/cypress.js | 4 ++-- cli/types/cypress.d.ts | 10 ++++++++- cli/types/tests/cypress-npm-api-test.ts | 30 ++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/cli/lib/cypress.js b/cli/lib/cypress.js index 376aa7c03b2..3eda50b7d4a 100644 --- a/cli/lib/cypress.js +++ b/cli/lib/cypress.js @@ -99,8 +99,8 @@ const cypressModuleApi = { * }) * * @see ../types/cypress-npm-api.d.ts - * @param {Cypress.ComponentFrameworkDefinition} config - * @returns {Cypress.ComponentFrameworkDefinition} the configuration passed in parameter + * @param {Cypress.ThirdPartyComponentFrameworkDefinition} config + * @returns {Cypress.ThirdPartyComponentFrameworkDefinition} the configuration passed in parameter */ defineComponentFrameworkDefinition (config) { return config diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 02c3c6d0210..4f336dab359 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -3405,7 +3405,15 @@ declare namespace Cypress { } // Certain properties are not supported for third party frameworks right now, such as ones related to the "Create From" feature. - type ThirdPartyComponentFrameworkDefinition = Omit + type ThirdPartyComponentFrameworkDefinition = Omit & { + type: `cypress-ct-${string}` + + /** + * Only `library` is supported for third party definitions. + * `template` will be supported in the future. + */ + category: 'library' + } interface AngularDevServerProjectConfig { root: string diff --git a/cli/types/tests/cypress-npm-api-test.ts b/cli/types/tests/cypress-npm-api-test.ts index 3a0feebd7df..2554004c034 100644 --- a/cli/types/tests/cypress-npm-api-test.ts +++ b/cli/types/tests/cypress-npm-api-test.ts @@ -1,6 +1,6 @@ // type tests for Cypress NPM module // https://on.cypress.io/module-api -import cypress, { defineConfig } from 'cypress' +import cypress, { defineComponentFrameworkDefinition, defineConfig } from 'cypress' cypress.run // $ExpectType (options?: Partial | undefined) => Promise cypress.open // $ExpectType (options?: Partial | undefined) => Promise @@ -55,6 +55,34 @@ const config = defineConfig({ modifyObstructiveCode: true }) +const solid + = { + type: 'solid-js', + name: 'Solid.js', + package: 'solid-js', + installer: 'solid-js', + description: 'Solid is a declarative JavaScript library for creating user interfaces', + minVersion: '^1.0.0', + } + +const thirdPartyFrameworkDefinition = defineComponentFrameworkDefinition({ + type: 'cypress-ct-third-party', + name: 'Third Party', + category: 'library', + dependencies: (bundler) => [solid], + detectors: [solid], + supportedBundlers: ['vite', 'webpack'], +}) + +const thirdPartyFrameworkDefinitionInvalidStrings = defineComponentFrameworkDefinition({ + type: 'cypress-not-cy-wrong-prefix', // $ExpectError + name: 'Third Party', + category: 'template', // $ExpectError - only library supported for third party definitions + dependencies: (bundler) => [], + detectors: [{}], // $ExpectError + supportedBundlers: ['metro', 'webpack'], // $ExpectError +}) + // component options const componentConfigNextWebpack: Cypress.ConfigOptions = { component: { From 270e4e7096d89c53a8dcb0a4a9c2f972c9846342 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Fri, 10 Feb 2023 14:50:59 +1000 Subject: [PATCH 29/40] lint --- cli/types/cypress.d.ts | 10 +++++----- cli/types/tests/cypress-npm-api-test.ts | 17 ++++++++--------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 4f336dab359..a76c37e0d24 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -3400,20 +3400,20 @@ declare namespace Cypress { specPattern?: '**/*.cy.ts' } - type ComponentFrameworkDefinition = Omit & { - dependencies: (bundler: 'webpack' | 'vite') => Cypress.CypressComponentDependency[] + type ComponentFrameworkDefinition = Omit & { + dependencies: (bundler: 'webpack' | 'vite') => CypressComponentDependency[] } // Certain properties are not supported for third party frameworks right now, such as ones related to the "Create From" feature. - type ThirdPartyComponentFrameworkDefinition = Omit & { + type ThirdPartyComponentFrameworkDefinition = Omit & { type: `cypress-ct-${string}` - /** + /** * Only `library` is supported for third party definitions. * `template` will be supported in the future. */ category: 'library' - } + } interface AngularDevServerProjectConfig { root: string diff --git a/cli/types/tests/cypress-npm-api-test.ts b/cli/types/tests/cypress-npm-api-test.ts index 2554004c034..41047650225 100644 --- a/cli/types/tests/cypress-npm-api-test.ts +++ b/cli/types/tests/cypress-npm-api-test.ts @@ -55,15 +55,14 @@ const config = defineConfig({ modifyObstructiveCode: true }) -const solid - = { - type: 'solid-js', - name: 'Solid.js', - package: 'solid-js', - installer: 'solid-js', - description: 'Solid is a declarative JavaScript library for creating user interfaces', - minVersion: '^1.0.0', - } +const solid = { + type: 'solid-js', + name: 'Solid.js', + package: 'solid-js', + installer: 'solid-js', + description: 'Solid is a declarative JavaScript library for creating user interfaces', + minVersion: '^1.0.0', +} const thirdPartyFrameworkDefinition = defineComponentFrameworkDefinition({ type: 'cypress-ct-third-party', From 5060900aca27afa10911df91c0f811ab1ea21745 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Fri, 10 Feb 2023 14:56:29 +1000 Subject: [PATCH 30/40] fix bug --- .../data-context/test/unit/sources/WizardDataSource.spec.ts | 4 ++-- packages/scaffold-config/src/frameworks.ts | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/data-context/test/unit/sources/WizardDataSource.spec.ts b/packages/data-context/test/unit/sources/WizardDataSource.spec.ts index 6833b71ff07..d29d5259447 100644 --- a/packages/data-context/test/unit/sources/WizardDataSource.spec.ts +++ b/packages/data-context/test/unit/sources/WizardDataSource.spec.ts @@ -1,8 +1,8 @@ -import { WizardBundler, ResolvedComponentFrameworkDefinition, WIZARD_BUNDLERS, CT_FRAMEWORKS, resolveComponentFrameworkDefinition } from '@packages/scaffold-config' +import { WizardBundler, WIZARD_BUNDLERS, CT_FRAMEWORKS, resolveComponentFrameworkDefinition } from '@packages/scaffold-config' import { expect } from 'chai' import { createTestDataContext, scaffoldMigrationProject, removeCommonNodeModules } from '../helper' -function findFramework (type: ResolvedComponentFrameworkDefinition['type']) { +function findFramework (type: Cypress.ResolvedComponentFrameworkDefinition['type']) { return resolveComponentFrameworkDefinition(CT_FRAMEWORKS.find((x) => x.type === type)!) } diff --git a/packages/scaffold-config/src/frameworks.ts b/packages/scaffold-config/src/frameworks.ts index 60385f31f6d..148b52ffae8 100644 --- a/packages/scaffold-config/src/frameworks.ts +++ b/packages/scaffold-config/src/frameworks.ts @@ -293,7 +293,9 @@ export function resolveComponentFrameworkDefinition (definition: Cypress.Compone const declaredDeps = definition.dependencies(bundler) // Must add bundler based on launchpad selection if it's a third party definition. - declaredDeps.push(getBundler(bundler)) + if (isThirdPartyDefinition) { + declaredDeps.push(getBundler(bundler)) + } return await Promise.all(declaredDeps.map((dep) => isDependencyInstalled(dep, projectPath))) } From 6d6d7181324829c307a7a70750de55d3562352d8 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Sat, 11 Feb 2023 00:07:20 +1000 Subject: [PATCH 31/40] update function name --- cli/lib/cypress.js | 6 +++--- cli/types/cypress-npm-api.d.ts | 6 +++--- cli/types/tests/cypress-npm-api-test.ts | 12 ++++++------ packages/scaffold-config/test/unit/fixtures.ts | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cli/lib/cypress.js b/cli/lib/cypress.js index 3eda50b7d4a..ff140859ff2 100644 --- a/cli/lib/cypress.js +++ b/cli/lib/cypress.js @@ -91,9 +91,9 @@ const cypressModuleApi = { /** * Provides automatic code completion for Component Frameworks Definitions. * While it's not strictly necessary for Cypress to parse your configuration, we - * recommend wrapping your Component Framework Definition object with `defineComponentFrameworkDefinition()` + * recommend wrapping your Component Framework Definition object with `defineComponentFramework()` * @example - * module.exports = defineComponentFrameworkDefinition({ + * module.exports = defineComponentFramework({ * type: 'cypress-ct-solid-js' * // ... * }) @@ -102,7 +102,7 @@ const cypressModuleApi = { * @param {Cypress.ThirdPartyComponentFrameworkDefinition} config * @returns {Cypress.ThirdPartyComponentFrameworkDefinition} the configuration passed in parameter */ - defineComponentFrameworkDefinition (config) { + defineComponentFramework (config) { return config }, } diff --git a/cli/types/cypress-npm-api.d.ts b/cli/types/cypress-npm-api.d.ts index 1066cb9ba89..7e3604f8316 100644 --- a/cli/types/cypress-npm-api.d.ts +++ b/cli/types/cypress-npm-api.d.ts @@ -397,9 +397,9 @@ declare module 'cypress' { /** * Provides automatic code completion for Component Frameworks Definitions. * While it's not strictly necessary for Cypress to parse your configuration, we - * recommend wrapping your Component Framework Definition object with `defineComponentFrameworkDefinition()` + * recommend wrapping your Component Framework Definition object with `defineComponentFramework()` * @example - * module.exports = defineComponentFrameworkDefinition({ + * module.exports = defineComponentFramework({ * type: 'cypress-ct-solid-js' * // ... * }) @@ -408,7 +408,7 @@ declare module 'cypress' { * @param {Cypress.ThirdPartyComponentFrameworkDefinition} config * @returns {Cypress.ThirdPartyComponentFrameworkDefinition} the configuration passed in parameter */ - defineComponentFrameworkDefinition (config: Cypress.ThirdPartyComponentFrameworkDefinition): Cypress.ThirdPartyComponentFrameworkDefinition + defineComponentFramework(config: Cypress.ThirdPartyComponentFrameworkDefinition): Cypress.ThirdPartyComponentFrameworkDefinition } // export Cypress NPM module interface diff --git a/cli/types/tests/cypress-npm-api-test.ts b/cli/types/tests/cypress-npm-api-test.ts index 41047650225..e5af3af9864 100644 --- a/cli/types/tests/cypress-npm-api-test.ts +++ b/cli/types/tests/cypress-npm-api-test.ts @@ -1,6 +1,6 @@ // type tests for Cypress NPM module // https://on.cypress.io/module-api -import cypress, { defineComponentFrameworkDefinition, defineConfig } from 'cypress' +import cypress, { defineComponentFramework, defineConfig } from 'cypress' cypress.run // $ExpectType (options?: Partial | undefined) => Promise cypress.open // $ExpectType (options?: Partial | undefined) => Promise @@ -61,25 +61,25 @@ const solid = { package: 'solid-js', installer: 'solid-js', description: 'Solid is a declarative JavaScript library for creating user interfaces', - minVersion: '^1.0.0', + minVersion: '^1.0.0' } -const thirdPartyFrameworkDefinition = defineComponentFrameworkDefinition({ +const thirdPartyFrameworkDefinition = defineComponentFramework({ type: 'cypress-ct-third-party', name: 'Third Party', category: 'library', dependencies: (bundler) => [solid], detectors: [solid], - supportedBundlers: ['vite', 'webpack'], + supportedBundlers: ['vite', 'webpack'] }) -const thirdPartyFrameworkDefinitionInvalidStrings = defineComponentFrameworkDefinition({ +const thirdPartyFrameworkDefinitionInvalidStrings = defineComponentFramework({ type: 'cypress-not-cy-wrong-prefix', // $ExpectError name: 'Third Party', category: 'template', // $ExpectError - only library supported for third party definitions dependencies: (bundler) => [], detectors: [{}], // $ExpectError - supportedBundlers: ['metro', 'webpack'], // $ExpectError + supportedBundlers: ['metro', 'webpack'] // $ExpectError }) // component options diff --git a/packages/scaffold-config/test/unit/fixtures.ts b/packages/scaffold-config/test/unit/fixtures.ts index 2cdf9400a40..ea4b6236588 100644 --- a/packages/scaffold-config/test/unit/fixtures.ts +++ b/packages/scaffold-config/test/unit/fixtures.ts @@ -1,4 +1,4 @@ -import { defineComponentFrameworkDefinition } from 'cypress' +import { defineComponentFramework } from 'cypress' export const solidDep: Cypress.CypressComponentDependency = { type: 'solid-js', @@ -10,7 +10,7 @@ export const solidDep: Cypress.CypressComponentDependency = { } // must be default export -export default defineComponentFrameworkDefinition({ +export default defineComponentFramework({ type: 'cypress-ct-solid-js', category: 'library', From 08b705af22496b2e1c33b9884d63722315f34d69 Mon Sep 17 00:00:00 2001 From: Zachary Williams Date: Fri, 10 Feb 2023 14:05:55 -0600 Subject: [PATCH 32/40] address feedback --- cli/types/cypress.d.ts | 2 +- cli/types/tests/cypress-npm-api-test.ts | 5 +- .../data-context/src/actions/WizardActions.ts | 2 +- .../src/data/ProjectConfigManager.ts | 2 +- .../frontend-shared/src/locales/en-US.json | 1 - packages/graphql/schemas/cloud.graphql | 50 +++++++++++++++++++ packages/graphql/schemas/schema.graphql | 48 +++++++++++++++++- .../schemaTypes/enumTypes/gql-WizardEnums.ts | 4 +- .../gql-WizardFrontendFramework.ts | 4 +- .../launchpad/src/setup/SelectFwOrBundler.vue | 4 +- packages/launchpad/src/setup/types.ts | 2 +- packages/scaffold-config/src/frameworks.ts | 2 +- 12 files changed, 112 insertions(+), 14 deletions(-) diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index c836a281d69..d11061a6fed 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -3406,7 +3406,7 @@ declare namespace Cypress { // Certain properties are not supported for third party frameworks right now, such as ones related to the "Create From" feature. type ThirdPartyComponentFrameworkDefinition = Omit & { - type: `cypress-ct-${string}` + type: string /** * Only `library` is supported for third party definitions. diff --git a/cli/types/tests/cypress-npm-api-test.ts b/cli/types/tests/cypress-npm-api-test.ts index e5af3af9864..85251d5b335 100644 --- a/cli/types/tests/cypress-npm-api-test.ts +++ b/cli/types/tests/cypress-npm-api-test.ts @@ -74,9 +74,10 @@ const thirdPartyFrameworkDefinition = defineComponentFramework({ }) const thirdPartyFrameworkDefinitionInvalidStrings = defineComponentFramework({ - type: 'cypress-not-cy-wrong-prefix', // $ExpectError + type: 'cypress-ct-third-party', name: 'Third Party', - category: 'template', // $ExpectError - only library supported for third party definitions + // only library supported for third party definitions + category: 'template', // $ExpectError dependencies: (bundler) => [], detectors: [{}], // $ExpectError supportedBundlers: ['metro', 'webpack'] // $ExpectError diff --git a/packages/data-context/src/actions/WizardActions.ts b/packages/data-context/src/actions/WizardActions.ts index 3e31e028e1e..ed2322c6edf 100644 --- a/packages/data-context/src/actions/WizardActions.ts +++ b/packages/data-context/src/actions/WizardActions.ts @@ -313,7 +313,7 @@ export class WizardActions { await this.ensureDir('support') - const defaultComponentIndex = componentIndexHtmlGenerator('') + const defaultComponentIndex = componentIndexHtmlGenerator() return this.scaffoldFile( componentIndexHtmlPath, diff --git a/packages/data-context/src/data/ProjectConfigManager.ts b/packages/data-context/src/data/ProjectConfigManager.ts index aab3fa0e371..58e2a3a2206 100644 --- a/packages/data-context/src/data/ProjectConfigManager.ts +++ b/packages/data-context/src/data/ProjectConfigManager.ts @@ -198,7 +198,7 @@ export class ProjectConfigManager { } const isFrameworkSatisfied = async (bundler: typeof WIZARD_BUNDLERS[number], framework: Cypress.ResolvedComponentFrameworkDefinition) => { - const deps = await (framework.dependencies(bundler.type, this.options.projectRoot)) + const deps = await framework.dependencies(bundler.type, this.options.projectRoot) debug('deps are %o', deps) diff --git a/packages/frontend-shared/src/locales/en-US.json b/packages/frontend-shared/src/locales/en-US.json index 4e3623f5191..3a887caf036 100644 --- a/packages/frontend-shared/src/locales/en-US.json +++ b/packages/frontend-shared/src/locales/en-US.json @@ -352,7 +352,6 @@ "frameworkLabel": "Front-end framework", "frameworkPlaceholder": "Pick a framework", "bundlerLabel": "Bundler", - "bundlerLabelDescription": "(dev server)", "bundlerPlaceholder": "Pick a bundler", "languageLabel": "Language", "configFileLanguageLabel": "Cypress config file", diff --git a/packages/graphql/schemas/cloud.graphql b/packages/graphql/schemas/cloud.graphql index 0dae909ab71..d3a9d5734d7 100644 --- a/packages/graphql/schemas/cloud.graphql +++ b/packages/graphql/schemas/cloud.graphql @@ -347,6 +347,16 @@ type CloudProjectSpec implements Node { """ fromBranch: String! ): Float + + """ + Average duration the spec takes to run within the context of the provided CloudRun ids. + """ + averageDurationForRunIds( + """ + The ids for the CloudRuns to use to derive the average duration. When provided, the fromBranch argument is ignored, as the provided run ids define the search space. + """ + cloudRunIds: [ID!]! + ): Float flakyStatus( """ The number of runs to consider when counting flaky runs. @@ -359,6 +369,16 @@ type CloudProjectSpec implements Node { fromBranch: String! ): CloudProjectSpecFlakyResult + """ + The flaky metadata for the spec within the context of the provided CloudRun ids. + """ + flakyStatusForRunIds( + """ + The ids for the CloudRuns, ordered from most to least relevant, to use to derive the flaky status. + """ + cloudRunIds: [ID!]! + ): CloudProjectSpecFlakyResult + """ Globally unique identifier representing a concrete GraphQL ObjectType """ @@ -370,6 +390,16 @@ type CloudProjectSpec implements Node { fromBranch: String! ): Boolean + """ + Indicator that a spec is considered flaky within the context of the provided CloudRun ids. + """ + isConsideredFlakyForRunIds( + """ + The ids for the CloudRuns to use to derive the flake indicator. When provided, the fromBranch argument is ignored, as the provided run ids define the search space. + """ + cloudRunIds: [ID!]! + ): Boolean + """ Current DateTime on the server. Used in connection with CloudLatestRunUpdateSpecData. """ @@ -445,6 +475,16 @@ type CloudProjectSpecFlakyStatus { The last flaky run occurrence, interpreted as "n runs ago" - ex: a value of 5 means a flaky run last occurred 5 runs ago """ lastFlaky: Int + + """ + The associated commit_sha of the CloudRun containing the most recent flaky occurrence of the spec. + """ + lastFlakyRunCommitSha: String + + """ + The run number of the CloudRun containing the most recent flaky occurrence of the spec. + """ + lastFlakyRunNumber: Int severity: String } @@ -520,6 +560,11 @@ type CloudRun implements Node { commitInfo: CloudRunCommitInfo completedAt: DateTime + """ + Number of specs instances that have been completed within a run + """ + completedInstanceCount: Int + """ When the run was created """ @@ -588,6 +633,11 @@ type CloudRun implements Node { """ totalFlakyTests: Int + """ + Number of instances within a run + """ + totalInstanceCount: Int + """ This is the number of passed tests across all groups in the run """ diff --git a/packages/graphql/schemas/schema.graphql b/packages/graphql/schemas/schema.graphql index c706683ba43..8121f807217 100644 --- a/packages/graphql/schemas/schema.graphql +++ b/packages/graphql/schemas/schema.graphql @@ -311,6 +311,16 @@ type CloudProjectSpec implements Node { """ fromBranch: String! ): Float + + """ + Average duration the spec takes to run within the context of the provided CloudRun ids. + """ + averageDurationForRunIds( + """ + The ids for the CloudRuns to use to derive the average duration. When provided, the fromBranch argument is ignored, as the provided run ids define the search space. + """ + cloudRunIds: [ID!]! + ): Float flakyStatus( """The number of runs to consider when counting flaky runs.""" flakyRunsWindow: Int! @@ -321,6 +331,16 @@ type CloudProjectSpec implements Node { fromBranch: String! ): CloudProjectSpecFlakyResult + """ + The flaky metadata for the spec within the context of the provided CloudRun ids. + """ + flakyStatusForRunIds( + """ + The ids for the CloudRuns, ordered from most to least relevant, to use to derive the flaky status. + """ + cloudRunIds: [ID!]! + ): CloudProjectSpecFlakyResult + """Globally unique identifier representing a concrete GraphQL ObjectType""" id: ID! isConsideredFlaky( @@ -328,6 +348,16 @@ type CloudProjectSpec implements Node { fromBranch: String! ): Boolean + """ + Indicator that a spec is considered flaky within the context of the provided CloudRun ids. + """ + isConsideredFlakyForRunIds( + """ + The ids for the CloudRuns to use to derive the flake indicator. When provided, the fromBranch argument is ignored, as the provided run ids define the search space. + """ + cloudRunIds: [ID!]! + ): Boolean + """ Current DateTime on the server. Used in connection with CloudLatestRunUpdateSpecData. """ @@ -380,6 +410,16 @@ type CloudProjectSpecFlakyStatus { The last flaky run occurrence, interpreted as "n runs ago" - ex: a value of 5 means a flaky run last occurred 5 runs ago """ lastFlaky: Int + + """ + The associated commit_sha of the CloudRun containing the most recent flaky occurrence of the spec. + """ + lastFlakyRunCommitSha: String + + """ + The run number of the CloudRun containing the most recent flaky occurrence of the spec. + """ + lastFlakyRunNumber: Int severity: String } @@ -430,6 +470,9 @@ type CloudRun implements Node { commitInfo: CloudRunCommitInfo completedAt: DateTime + """Number of specs instances that have been completed within a run""" + completedInstanceCount: Int + """When the run was created""" createdAt: DateTime! @@ -480,6 +523,9 @@ type CloudRun implements Node { """Number of flaky tests, null if flake detection is not enabled""" totalFlakyTests: Int + """Number of instances within a run""" + totalInstanceCount: Int + """This is the number of passed tests across all groups in the run""" totalPassed: Int @@ -2351,7 +2397,7 @@ type WizardFrontendFramework implements Node { name: String! """Current support status of the framework""" - supportStatus: String! + supportStatus: SupportStatusEnum! """All of the supported bundlers for this framework""" supportedBundlers: [WizardBundler!]! diff --git a/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts b/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts index ded4b744bf1..c8fd1d02c70 100644 --- a/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts +++ b/packages/graphql/src/schemaTypes/enumTypes/gql-WizardEnums.ts @@ -1,5 +1,5 @@ import { CODE_LANGUAGES } from '@packages/types' -import { WIZARD_BUNDLERS } from '@packages/scaffold-config' +import { WIZARD_BUNDLERS, SUPPORT_STATUSES } from '@packages/scaffold-config' import { enumType } from 'nexus' export const SupportedBundlerEnum = enumType({ @@ -15,7 +15,7 @@ export const WizardConfigFileStatusEnum = enumType({ export const SupportStatusEnum = enumType({ name: 'SupportStatusEnum', - members: ['alpha', 'beta', 'full', 'community'], + members: SUPPORT_STATUSES, }) export const CodeLanguageEnum = enumType({ diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts index c12968c8952..98df48562b3 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts @@ -1,6 +1,7 @@ import { WizardBundler } from './gql-WizardBundler' import { objectType } from 'nexus' import { WIZARD_BUNDLERS } from '@packages/scaffold-config' +import { SupportStatusEnum } from '../enumTypes' export const WizardFrontendFramework = objectType({ name: 'WizardFrontendFramework', @@ -19,8 +20,9 @@ export const WizardFrontendFramework = objectType({ description: 'The display name of the framework', }) - t.nonNull.string('supportStatus', { + t.nonNull.field('supportStatus', { description: 'Current support status of the framework', + type: SupportStatusEnum, }) t.nonNull.boolean('isSelected', { diff --git a/packages/launchpad/src/setup/SelectFwOrBundler.vue b/packages/launchpad/src/setup/SelectFwOrBundler.vue index caf3f864ccd..4d22dbfba70 100644 --- a/packages/launchpad/src/setup/SelectFwOrBundler.vue +++ b/packages/launchpad/src/setup/SelectFwOrBundler.vue @@ -34,7 +34,7 @@ {{ selectedOptionName }} - + - + { return semver.major(reactPkg.detectedVersion) === 18 ? 'cypress/react18' : 'cypress/react' } -export const supportStatus = ['alpha', 'beta', 'full', 'community'] as const +export const SUPPORT_STATUSES: Readonly = ['alpha', 'beta', 'full', 'community'] as const export const CT_FRAMEWORKS: Cypress.ComponentFrameworkDefinition[] = [ { From 2c3a2d8b889089428ccecc8ea84e1f5b3c19df43 Mon Sep 17 00:00:00 2001 From: Zachary Williams Date: Wed, 8 Feb 2023 14:56:17 -0600 Subject: [PATCH 33/40] feat: scan for 3rd party ct plugins --- cli/types/cypress.d.ts | 6 ++ .../data-context/src/actions/WizardActions.ts | 13 ++- .../data-context/src/data/coreDataShape.ts | 4 +- packages/graphql/schemas/schema.graphql | 3 + .../gql-WizardFrontendFramework.ts | 4 + .../launchpad/src/setup/EnvironmentSetup.vue | 3 + .../launchpad/src/setup/SelectFwOrBundler.vue | 12 +++ packages/launchpad/src/setup/types.ts | 1 + packages/scaffold-config/package.json | 3 +- .../src/ct-detect-third-party.ts | 100 ++++++++++++++++++ packages/scaffold-config/src/index.ts | 1 + yarn.lock | 5 + 12 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 packages/scaffold-config/src/ct-detect-third-party.ts diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index d11061a6fed..e104fe24721 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -3398,6 +3398,12 @@ declare namespace Cypress { * @internal */ specPattern?: '**/*.cy.ts' + + /** + * Raw SVG icon that will be displayed in the Project Setup Wizard. Used for third parties that + * want to render a custom icon. + */ + icon?: string } type ComponentFrameworkDefinition = Omit & { diff --git a/packages/data-context/src/actions/WizardActions.ts b/packages/data-context/src/actions/WizardActions.ts index ed2322c6edf..4a2bcbf03c7 100644 --- a/packages/data-context/src/actions/WizardActions.ts +++ b/packages/data-context/src/actions/WizardActions.ts @@ -1,5 +1,5 @@ import type { NexusGenObjects } from '@packages/graphql/src/gen/nxs.gen' -import { detectFramework, commandsFileBody, supportFileComponent, supportFileE2E, getBundler } from '@packages/scaffold-config' +import { detectFramework, commandsFileBody, supportFileComponent, supportFileE2E, getBundler, detectThirdPartyCTFrameworks, resolveComponentFrameworkDefinition, CT_FRAMEWORKS } from '@packages/scaffold-config' import assert from 'assert' import path from 'path' import Debug from 'debug' @@ -100,6 +100,8 @@ export class WizardActions { this.resetWizard() + await this.detectFrameworks(this.ctx.currentProject) + const detected = await detectFramework(this.ctx.currentProject, this.ctx.coreData.wizard.frameworks) debug('detected %o', detected) @@ -361,6 +363,15 @@ export class WizardActions { private ensureDir (type: 'e2e' | 'fixtures' | 'support') { return this.ctx.fs.ensureDir(path.join(this.projectRoot, 'cypress', type)) } + + private async detectFrameworks (projectRoot: string) { + const officialFrameworks = CT_FRAMEWORKS.map((framework) => resolveComponentFrameworkDefinition(framework)) + const thirdPartyFrameworks = await detectThirdPartyCTFrameworks(projectRoot).then((frameworks) => frameworks.map(resolveComponentFrameworkDefinition)) + + this.ctx.update((d) => { + d.wizard.frameworks = officialFrameworks.concat(thirdPartyFrameworks) + }) + } } const FIXTURE_DATA = { diff --git a/packages/data-context/src/data/coreDataShape.ts b/packages/data-context/src/data/coreDataShape.ts index 3bed2cc4860..ff69eb8f17f 100644 --- a/packages/data-context/src/data/coreDataShape.ts +++ b/packages/data-context/src/data/coreDataShape.ts @@ -1,5 +1,5 @@ import { FoundBrowser, Editor, AllowedState, AllModeOptions, TestingType, BrowserStatus, PACKAGE_MANAGERS, AuthStateName, MIGRATION_STEPS, MigrationStep, BannerState } from '@packages/types' -import { WizardBundler, CT_FRAMEWORKS, resolveComponentFrameworkDefinition } from '@packages/scaffold-config' +import type { WizardBundler } from '@packages/scaffold-config' import type { NexusGenObjects } from '@packages/graphql/src/gen/nxs.gen' import type { App, BrowserWindow } from 'electron' import type { ChildProcess } from 'child_process' @@ -199,7 +199,7 @@ export function makeCoreData (modeOptions: Partial = {}): CoreDa detectedBundler: null, detectedFramework: null, // TODO: API to add third party frameworks to this list. - frameworks: CT_FRAMEWORKS.map((framework) => resolveComponentFrameworkDefinition(framework)), + frameworks: [], }, migration: { step: 'renameAuto', diff --git a/packages/graphql/schemas/schema.graphql b/packages/graphql/schemas/schema.graphql index 8121f807217..19f2426f327 100644 --- a/packages/graphql/schemas/schema.graphql +++ b/packages/graphql/schemas/schema.graphql @@ -2384,6 +2384,9 @@ type WizardFrontendFramework implements Node { """The category (framework, like react-scripts, or library, like react""" category: String! + """Raw SVG icon of framework""" + icon: String + """Relay style Node ID field for the WizardFrontendFramework field""" id: ID! diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts index 98df48562b3..d61c186f5dd 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-WizardFrontendFramework.ts @@ -52,5 +52,9 @@ export const WizardFrontendFramework = objectType({ return ctx.wizardData.chosenFramework?.supportedBundlers.map(findBundler) ?? [] }, }) + + t.string('icon', { + description: 'Raw SVG icon of framework', + }) }, }) diff --git a/packages/launchpad/src/setup/EnvironmentSetup.vue b/packages/launchpad/src/setup/EnvironmentSetup.vue index dbd58ef62b8..8241ad9344f 100644 --- a/packages/launchpad/src/setup/EnvironmentSetup.vue +++ b/packages/launchpad/src/setup/EnvironmentSetup.vue @@ -65,6 +65,7 @@ fragment EnvironmentSetup on Wizard { isDetected } category + icon } frameworks { id @@ -73,6 +74,7 @@ fragment EnvironmentSetup on Wizard { isDetected type category + icon } allBundlers { id @@ -104,6 +106,7 @@ const frameworks = computed(() => { name: x.name, id: x.id, isDetected: x.isDetected, + icon: x.icon || undefined, } }) diff --git a/packages/launchpad/src/setup/SelectFwOrBundler.vue b/packages/launchpad/src/setup/SelectFwOrBundler.vue index 4d22dbfba70..43c0e93f8e7 100644 --- a/packages/launchpad/src/setup/SelectFwOrBundler.vue +++ b/packages/launchpad/src/setup/SelectFwOrBundler.vue @@ -25,8 +25,14 @@ > +