diff --git a/packages/app/cypress/e2e/create-from-component.cy.ts b/packages/app/cypress/e2e/create-from-component.cy.ts
new file mode 100644
index 000000000000..5216f35ce907
--- /dev/null
+++ b/packages/app/cypress/e2e/create-from-component.cy.ts
@@ -0,0 +1,112 @@
+import defaultMessages from '@packages/frontend-shared/src/locales/en-US.json'
+import { getPathForPlatform } from '../../src/paths'
+
+function validateCreateFromComponentCard (beforeEachFn: () => void, expectedSpecPath: string) {
+ beforeEach(beforeEachFn)
+
+ it('Shows create from component card for Vue projects with default spec patterns', () => {
+ cy.get('@ComponentCard')
+ .within(() => {
+ cy.findByRole('button', {
+ name: 'Create from component',
+ }).should('be.visible')
+ .and('not.be.disabled')
+ })
+ })
+
+ it('Can be closed with the x button', () => {
+ cy.get('@ComponentCard').click()
+
+ cy.findByRole('button', { name: 'Close' }).as('DialogCloseButton')
+
+ cy.get('@DialogCloseButton').click()
+ cy.findByRole('dialog', {
+ name: 'Choose a component',
+ }).should('not.exist')
+ })
+
+ it('Lists Vue components in the project', () => {
+ cy.get('@ComponentCard').click()
+
+ cy.findByText('2 Matches').should('be.visible')
+
+ cy.findByText('App').should('be.visible')
+ cy.findByText('HelloWorld').should('be.visible')
+ })
+
+ it('Allows for the user to search through their components', () => {
+ cy.get('@ComponentCard').click()
+
+ cy.findByText('*.vue').should('be.visible')
+ cy.findByText('2 Matches').should('be.visible')
+ cy.findByLabelText('file-name-input').type('HelloWorld')
+
+ cy.findByText('HelloWorld').should('be.visible')
+ cy.findByText('1 of 2 Matches').should('be.visible')
+ cy.findByText('App').should('not.exist')
+ })
+
+ it('shows success modal when component spec is created', () => {
+ cy.get('@ComponentCard').click()
+
+ cy.findByText('HelloWorld').should('be.visible').click()
+
+ cy.findByRole('dialog', {
+ name: defaultMessages.createSpec.successPage.header,
+ }).as('SuccessDialog').within(() => {
+ cy.contains(getPathForPlatform(expectedSpecPath)).should('be.visible')
+ cy.findByRole('button', { name: 'Close' }).should('be.visible')
+
+ cy.findByRole('link', { name: 'Okay, run the spec' })
+ .should('have.attr', 'href', `#/specs/runner?file=${expectedSpecPath}`)
+
+ cy.findByRole('button', { name: 'Create another spec' }).click()
+ })
+
+ // 'Create from component' card appears again when the user selects "create another spec"
+ cy.findByText('Create from component').should('be.visible')
+ })
+
+ it('runs generated spec', () => {
+ cy.get('@ComponentCard').click()
+
+ cy.findByText('HelloWorld').should('be.visible').click()
+
+ cy.findByRole('dialog', {
+ name: defaultMessages.createSpec.successPage.header,
+ }).as('SuccessDialog').within(() => {
+ cy.contains(getPathForPlatform(expectedSpecPath)).should('be.visible')
+ cy.findByRole('button', { name: 'Close' }).should('be.visible')
+
+ cy.findByRole('link', { name: 'Okay, run the spec' })
+ .should('have.attr', 'href', `#/specs/runner?file=${expectedSpecPath}`).click()
+ })
+
+ cy.findByText('').should('be.visible')
+ })
+}
+
+describe('Create from component card', () => {
+ context('project with default spec pattern', () => {
+ validateCreateFromComponentCard(() => {
+ cy.scaffoldProject('no-specs-vue-2')
+ cy.openProject('no-specs-vue-2')
+ cy.startAppServer('component')
+ cy.visitApp()
+
+ cy.findAllByTestId('card').eq(0).as('ComponentCard')
+ }, 'src/components/HelloWorld.cy.js')
+ })
+
+ context('project with custom spec pattern', () => {
+ validateCreateFromComponentCard(() => {
+ cy.scaffoldProject('no-specs-vue-2')
+ cy.openProject('no-specs-vue-2', ['--config-file', 'cypress-custom-spec-pattern.config.js'])
+ cy.startAppServer('component')
+ cy.visitApp()
+
+ cy.findByText('New Spec').click()
+ cy.findAllByTestId('card').eq(0).as('ComponentCard')
+ }, 'src/specs-folder/HelloWorld.cy.js')
+ })
+})
diff --git a/packages/app/cypress/e2e/specs.cy.ts b/packages/app/cypress/e2e/specs.cy.ts
index 97c5aa101c38..33397bbbd222 100644
--- a/packages/app/cypress/e2e/specs.cy.ts
+++ b/packages/app/cypress/e2e/specs.cy.ts
@@ -640,98 +640,6 @@ describe('App: Specs', () => {
})
})
- context('Create from component card', () => {
- beforeEach(() => {
- cy.scaffoldProject('no-specs-vue-2')
- cy.openProject('no-specs-vue-2')
- cy.startAppServer('component')
- cy.visitApp()
-
- cy.findAllByTestId('card').eq(0).as('ComponentCard')
- })
-
- it('Shows create from component card for Vue projects with default spec patterns', () => {
- cy.get('@ComponentCard')
- .within(() => {
- cy.findByRole('button', {
- name: 'Create from component',
- }).should('be.visible')
- .and('not.be.disabled')
- })
- })
-
- it('Can be closed with the x button', () => {
- cy.get('@ComponentCard').click()
-
- cy.findByRole('button', { name: 'Close' }).as('DialogCloseButton')
-
- cy.get('@DialogCloseButton').click()
- cy.findByRole('dialog', {
- name: 'Choose a component',
- }).should('not.exist')
- })
-
- it('Lists Vue components in the project', () => {
- cy.get('@ComponentCard').click()
-
- cy.findByText('2 Matches').should('be.visible')
-
- cy.findByText('App').should('be.visible')
- cy.findByText('HelloWorld').should('be.visible')
- })
-
- it('Allows for the user to search through their components', () => {
- cy.get('@ComponentCard').click()
-
- cy.findByText('*.vue').should('be.visible')
- cy.findByText('2 Matches').should('be.visible')
- cy.findByLabelText('file-name-input').type('HelloWorld')
-
- cy.findByText('HelloWorld').should('be.visible')
- cy.findByText('1 of 2 Matches').should('be.visible')
- cy.findByText('App').should('not.exist')
- })
-
- it('shows success modal when component spec is created', () => {
- cy.get('@ComponentCard').click()
-
- cy.findByText('HelloWorld').should('be.visible').click()
-
- cy.findByRole('dialog', {
- name: defaultMessages.createSpec.successPage.header,
- }).as('SuccessDialog').within(() => {
- cy.contains(getPathForPlatform('src/components/HelloWorld.cy.js')).should('be.visible')
- cy.findByRole('button', { name: 'Close' }).should('be.visible')
-
- cy.findByRole('link', { name: 'Okay, run the spec' })
- .should('have.attr', 'href', `#/specs/runner?file=src/components/HelloWorld.cy.js`)
-
- cy.findByRole('button', { name: 'Create another spec' }).click()
- })
-
- // 'Create from component' card appears again when the user selects "create another spec"
- cy.findByText('Create from component').should('be.visible')
- })
-
- it('runs generated spec', () => {
- cy.get('@ComponentCard').click()
-
- cy.findByText('HelloWorld').should('be.visible').click()
-
- cy.findByRole('dialog', {
- name: defaultMessages.createSpec.successPage.header,
- }).as('SuccessDialog').within(() => {
- cy.contains(getPathForPlatform('src/components/HelloWorld.cy.js')).should('be.visible')
- cy.findByRole('button', { name: 'Close' }).should('be.visible')
-
- cy.findByRole('link', { name: 'Okay, run the spec' })
- .should('have.attr', 'href', `#/specs/runner?file=src/components/HelloWorld.cy.js`).click()
- })
-
- cy.findByText('').should('be.visible')
- })
- })
-
context('project with custom spec pattern', () => {
beforeEach(() => {
cy.scaffoldProject('no-specs-custom-pattern')
diff --git a/packages/app/src/specs/CreateSpecModal.cy.tsx b/packages/app/src/specs/CreateSpecModal.cy.tsx
index f2f79b26cc6f..4d094591881f 100644
--- a/packages/app/src/specs/CreateSpecModal.cy.tsx
+++ b/packages/app/src/specs/CreateSpecModal.cy.tsx
@@ -34,7 +34,6 @@ describe('', () => {
specPattern: '**/*.cy.{js,jsx,ts,tsx}',
},
}],
- isDefaultSpecPattern: true,
specs: [],
fileExtensionToUse: 'js',
defaultSpecFileName: 'cypress/e2e/ComponentName.cy.js',
@@ -130,7 +129,6 @@ describe('Modal Text Input', () => {
specPattern: '**/*.cy.{js,jsx,ts,tsx}',
},
}],
- isDefaultSpecPattern: true,
specs: [],
fileExtensionToUse: 'js',
defaultSpecFileName: 'cypress/e2e/ComponentName.cy.js',
@@ -179,7 +177,6 @@ describe('Modal Text Input', () => {
specPattern: '**/*.cy.{js,jsx,ts,tsx}',
},
}],
- isDefaultSpecPattern: true,
specs: [],
fileExtensionToUse: 'js',
defaultSpecFileName: 'this/path/does/not/produce/regex/match-',
@@ -232,7 +229,6 @@ describe('playground', () => {
specPattern: '**/*.cy.{js,jsx,ts,tsx}',
},
}],
- isDefaultSpecPattern: true,
specs: [],
fileExtensionToUse: 'js',
defaultSpecFileName: 'cypress/e2e/ComponentName.cy.js',
diff --git a/packages/app/src/specs/CreateSpecModal.vue b/packages/app/src/specs/CreateSpecModal.vue
index 13e8479934b6..a7abc4146365 100644
--- a/packages/app/src/specs/CreateSpecModal.vue
+++ b/packages/app/src/specs/CreateSpecModal.vue
@@ -77,7 +77,6 @@ fragment CreateSpecModal on Query {
id
fileExtensionToUse
defaultSpecFileName
- isDefaultSpecPattern
...ComponentGeneratorStepOne_codeGenGlob
...EmptyGenerator
}
@@ -107,7 +106,7 @@ const specFileName = computed(() => {
return getPathForPlatform(props.gql.currentProject?.defaultSpecFileName || '')
})
-const filteredGenerators = getFilteredGeneratorList(props.gql.currentProject, props.gql.currentProject?.isDefaultSpecPattern)
+const filteredGenerators = getFilteredGeneratorList(props.gql.currentProject)
const singleGenerator = computed(() => filteredGenerators.value.length === 1 ? filteredGenerators.value[0] : null)
diff --git a/packages/app/src/specs/DefaultSpecPatternNoContent.vue b/packages/app/src/specs/DefaultSpecPatternNoContent.vue
index 3f0569ea04a3..d8e2372b9e3e 100644
--- a/packages/app/src/specs/DefaultSpecPatternNoContent.vue
+++ b/packages/app/src/specs/DefaultSpecPatternNoContent.vue
@@ -63,7 +63,7 @@ const props = defineProps<{
gql: CreateSpecContentFragment
}>()
-const filteredGenerators = getFilteredGeneratorList(props.gql.currentProject, true)
+const filteredGenerators = getFilteredGeneratorList(props.gql.currentProject)
const emit = defineEmits<{
(e: 'showCreateSpecModal', id: string): void
diff --git a/packages/app/src/specs/generators/EmptyGenerator.vue b/packages/app/src/specs/generators/EmptyGenerator.vue
index a90b8b443946..180f1517f748 100644
--- a/packages/app/src/specs/generators/EmptyGenerator.vue
+++ b/packages/app/src/specs/generators/EmptyGenerator.vue
@@ -146,7 +146,6 @@ const props = defineProps<{
gql: EmptyGeneratorFragment
type: 'e2e' | 'component' | 'componentEmpty'
specFileName: string
- erroredCodegenCandidate?: string
/** is there any other generator available when clicking "Back" */
otherGenerators: boolean
}>()
@@ -168,8 +167,8 @@ mutation EmptyGenerator_MatchSpecFile($specFile: String!) {
`
gql`
-mutation EmptyGenerator_generateSpec($codeGenCandidate: String!, $type: CodeGenType!, $erroredCodegenCandidate: String) {
- generateSpecFromSource(codeGenCandidate: $codeGenCandidate, type: $type, erroredCodegenCandidate: $erroredCodegenCandidate) {
+mutation EmptyGenerator_generateSpec($codeGenCandidate: String!, $type: CodeGenType!) {
+ generateSpecFromSource(codeGenCandidate: $codeGenCandidate, type: $type) {
...GeneratorSuccess
}
}`
@@ -230,7 +229,7 @@ const createSpec = async () => {
return
}
- const { data } = await writeFile.executeMutation({ codeGenCandidate: specFile.value, type: props.type, erroredCodegenCandidate: props.erroredCodegenCandidate ?? null })
+ const { data } = await writeFile.executeMutation({ codeGenCandidate: specFile.value, type: props.type })
result.value = data?.generateSpecFromSource?.generatedSpecResult?.__typename === 'ScaffoldedFile' ? data?.generateSpecFromSource?.generatedSpecResult : null
}
diff --git a/packages/app/src/specs/generators/component/ComponentGenerator.tsx b/packages/app/src/specs/generators/component/ComponentGenerator.tsx
index 91b3236bdff1..1937264008b9 100644
--- a/packages/app/src/specs/generators/component/ComponentGenerator.tsx
+++ b/packages/app/src/specs/generators/component/ComponentGenerator.tsx
@@ -6,11 +6,7 @@ import ComponentGeneratorCard from './ComponentGeneratorCard.vue'
export const ComponentGenerator: SpecGenerator = {
card: ComponentGeneratorCard,
entry: ComponentGeneratorStepOne,
- show: (currentProject, isDefaultSpecPattern) => {
- if (!isDefaultSpecPattern) {
- return false
- }
-
+ show: (currentProject) => {
return currentProject?.codeGenGlobs?.component === '*.vue'
},
matches: filters.matchesCT,
diff --git a/packages/app/src/specs/generators/component/ComponentGeneratorStepOne.vue b/packages/app/src/specs/generators/component/ComponentGeneratorStepOne.vue
index 5f67b556d3f1..84ea5f54e2c3 100644
--- a/packages/app/src/specs/generators/component/ComponentGeneratorStepOne.vue
+++ b/packages/app/src/specs/generators/component/ComponentGeneratorStepOne.vue
@@ -7,7 +7,6 @@
type="component"
:other-generators="false"
:spec-file-name="generatedSpecError.fileName"
- :errored-codegen-candidate="generatedSpecError.erroredCodegenCandidate"
@restart="cancelSpecNameCreation"
@updateTitle="(value) => emits('update:title', value)"
/>
@@ -143,7 +142,6 @@ mutation ComponentGeneratorStepOne_generateSpec($codeGenCandidate: String!, $typ
generatedSpecResult {
... on GeneratedSpecError {
fileName
- erroredCodegenCandidate
}
}
}
diff --git a/packages/app/src/specs/generators/index.ts b/packages/app/src/specs/generators/index.ts
index 26bdd0013c30..6cf4061bc794 100644
--- a/packages/app/src/specs/generators/index.ts
+++ b/packages/app/src/specs/generators/index.ts
@@ -19,8 +19,8 @@ export const generatorList: SpecGenerator[] = [
EmptyGenerator,
]
-export const getFilteredGeneratorList = (currentProject, isDefaultSpecPattern) => {
- return computed(() => generatorList.filter((g) => g.matches(currentProject.currentTestingType) && (g.show === undefined ? true : g.show(currentProject, isDefaultSpecPattern))))
+export const getFilteredGeneratorList = (currentProject) => {
+ return computed(() => generatorList.filter((g) => g.matches(currentProject.currentTestingType) && (g.show === undefined ? true : g.show(currentProject))))
}
export const generators = keyBy(generatorList, 'id') as Record
diff --git a/packages/app/src/specs/generators/types.ts b/packages/app/src/specs/generators/types.ts
index ebb70a715e33..e8e3978098b4 100644
--- a/packages/app/src/specs/generators/types.ts
+++ b/packages/app/src/specs/generators/types.ts
@@ -15,6 +15,6 @@ export interface SpecGenerator {
card: Component
entry: Component
matches: (testingType?: TestingType | null) => boolean
- show: (currentProject?: CurrentProject, isDefaultSpecPattern?: boolean) => boolean
+ show: (currentProject?: CurrentProject) => boolean
id: GeneratorId
}
diff --git a/packages/data-context/src/actions/ProjectActions.ts b/packages/data-context/src/actions/ProjectActions.ts
index ef57970327cd..a24a15309c24 100644
--- a/packages/data-context/src/actions/ProjectActions.ts
+++ b/packages/data-context/src/actions/ProjectActions.ts
@@ -336,15 +336,13 @@ export class ProjectActions {
this.api.insertProjectPreferencesToCache(this.ctx.lifecycleManager.projectTitle, args)
}
- async codeGenSpec (codeGenCandidate: string, codeGenType: CodeGenType, erroredCodegenCandidate?: string | null): Promise {
+ async codeGenSpec (codeGenCandidate: string, codeGenType: CodeGenType): Promise {
const project = this.ctx.currentProject
- if (!project) {
- throw Error(`Cannot create spec without currentProject.`)
- }
+ assert(project, 'Cannot create spec without currentProject.')
const getCodeGenPath = () => {
- return codeGenType === 'e2e' || erroredCodegenCandidate
+ return codeGenType === 'e2e'
? this.ctx.path.join(
project,
codeGenCandidate,
@@ -354,18 +352,22 @@ export class ProjectActions {
const codeGenPath = getCodeGenPath()
+ const { specPattern = [] } = await this.ctx.project.specPatterns()
+
const newSpecCodeGenOptions = new SpecOptions({
codeGenPath,
codeGenType,
- erroredCodegenCandidate,
framework: this.getWizardFrameworkFromConfig(),
isDefaultSpecPattern: await this.ctx.project.getIsDefaultSpecPattern(),
+ specPattern,
+ currentProject: this.ctx.currentProject,
+ specs: this.ctx.project.specs,
})
let codeGenOptions = await newSpecCodeGenOptions.getCodeGenOptions()
const codeGenResults = await codeGenerator(
- { templateDir: templates[codeGenOptions.templateKey], target: path.parse(codeGenPath).dir },
+ { templateDir: templates[codeGenOptions.templateKey], target: codeGenOptions.overrideCodeGenDir || path.parse(codeGenPath).dir },
codeGenOptions,
)
diff --git a/packages/data-context/src/codegen/spec-options.ts b/packages/data-context/src/codegen/spec-options.ts
index c5c6226140b6..d0f0d14a85b0 100644
--- a/packages/data-context/src/codegen/spec-options.ts
+++ b/packages/data-context/src/codegen/spec-options.ts
@@ -3,14 +3,18 @@ import type { CodeGenType } from '@packages/graphql/src/gen/nxs.gen'
import type { WizardFrontendFramework } from '@packages/scaffold-config'
import fs from 'fs-extra'
import path from 'path'
+import { getDefaultSpecFileName } from '../sources/migration/utils'
+import { toPosix } from '../util'
+import type { FoundSpec } from '@packages/types'
interface CodeGenOptions {
codeGenPath: string
codeGenType: CodeGenType
isDefaultSpecPattern: boolean
- erroredCodegenCandidate?: string | null
- specFileExtension?: string
+ specPattern: string[]
+ currentProject: string | null
framework?: WizardFrontendFramework
+ specs?: FoundSpec[]
}
// Spec file extensions that we will preserve when updating the file name
@@ -26,14 +30,9 @@ type ComponentExtension = `.cy.${'js' | 'ts' | 'jsx' | 'tsx'}`
type TemplateKey = 'e2e' | 'componentEmpty' | 'vueComponent'
export class SpecOptions {
private parsedPath: ParsedPath;
- private parsedErroredCodegenCandidate?: ParsedPath
constructor (private options: CodeGenOptions) {
this.parsedPath = path.parse(options.codeGenPath)
-
- if (options.erroredCodegenCandidate) {
- this.parsedErroredCodegenCandidate = path.parse(options.erroredCodegenCandidate)
- }
}
async getCodeGenOptions () {
@@ -45,6 +44,7 @@ export class SpecOptions {
codeGenType: this.options.codeGenType,
fileName: await this.buildFileName(),
templateKey: this.options.codeGenType as TemplateKey,
+ overrideCodeGenDir: '',
}
}
@@ -53,12 +53,13 @@ export class SpecOptions {
throw new Error('Cannot generate a spec without a framework')
}
- // This only works for Vue projects with default spec patterns right now. If the framework is not Vue, we're generating an empty component test
- if (this.options.framework.codeGenFramework !== 'vue' || !this.options.isDefaultSpecPattern) {
+ // This only works for Vue projects right now. If the framework is not Vue, we're generating an empty component test
+ if (this.options.framework.codeGenFramework !== 'vue') {
return {
codeGenType: this.options.codeGenType,
fileName: await this.buildFileName(),
templateKey: 'componentEmpty' as TemplateKey,
+ overrideCodeGenDir: '',
}
}
@@ -67,29 +68,46 @@ export class SpecOptions {
return frameworkOptions
}
- private relativePath () {
- if (!this.parsedErroredCodegenCandidate?.base) {
- return `./${this.parsedPath.base}`
- }
+ private getRelativePathToComponent (specParsedPath?: ParsedPath) {
+ if (specParsedPath) {
+ const componentPathRelative = path.relative(specParsedPath.dir, this.parsedPath.dir)
- const componentPathRelative = path.relative(this.parsedPath.dir, this.parsedErroredCodegenCandidate.dir)
+ const componentPath = path.join(componentPathRelative, this.parsedPath.base)
- const componentPath = path.join(componentPathRelative, this.parsedErroredCodegenCandidate.base)
+ return toPosix(componentPath.startsWith('.') ? componentPath : `./${componentPath}`)
+ }
- return componentPath.startsWith('.') ? componentPath : `./${componentPath}`
+ return `./${this.parsedPath.base}`
}
private async getFrameworkComponentOptions () {
- const componentName = this.parsedErroredCodegenCandidate?.name ?? this.parsedPath.name
+ const componentName = this.parsedPath.name
+
+ const extension = await this.getVueExtension()
+
+ let parsedSpecPath: ParsedPath | undefined
+
+ // If we have a custom spec pattern, write the spec to a path that matches the pattern instead of the component directory
+ if (!this.options.isDefaultSpecPattern) {
+ parsedSpecPath = path.parse(await getDefaultSpecFileName({
+ currentProject: this.options.currentProject,
+ testingType: this.options.codeGenType === 'componentEmpty' || this.options.codeGenType === 'component' ? 'component' : 'e2e',
+ fileExtensionToUse: (extension === '.cy.ts' || extension === '.cy.tsx') ? 'ts' : 'js',
+ specPattern: this.options.specPattern,
+ name: componentName,
+ specs: this.options.specs }))
+ }
- const componentPath = this.relativePath()
+ // The path to import the component from
+ const componentPath = this.getRelativePathToComponent(parsedSpecPath)
return {
codeGenType: this.options.codeGenType,
componentName,
componentPath,
- fileName: await this.buildComponentSpecFilename(await this.getVueExtension()),
+ fileName: await this.buildComponentSpecFilename(extension, parsedSpecPath),
templateKey: 'vueComponent' as TemplateKey,
+ overrideCodeGenDir: parsedSpecPath?.dir,
}
}
@@ -111,24 +129,24 @@ export class SpecOptions {
}
}
- private getSpecExtension = () => {
- if (this.options.erroredCodegenCandidate) {
- return ''
- }
-
+ private getSpecExtension = (filePath?: ParsedPath) => {
const foundSpecExtension = expectedSpecExtensions.find((specExtension) => {
- return this.parsedPath.base.endsWith(specExtension + this.parsedPath.ext)
+ return filePath ? filePath.base.endsWith(specExtension + filePath.ext) :
+ this.parsedPath.base.endsWith(specExtension + this.parsedPath.ext)
})
return foundSpecExtension || ''
}
- private async buildComponentSpecFilename (specExt: string) {
- const { dir, base, ext } = this.parsedPath
- const cyWithExt = this.getSpecExtension() + specExt
- const name = base.slice(0, -ext.length)
+ private buildComponentSpecFilename (specExt: string, filePath?: ParsedPath) {
+ const { dir, base, ext } = filePath || this.parsedPath
+ const cyWithExt = this.getSpecExtension(filePath) + ext
- return this.getFinalFileName(dir, name, cyWithExt, path.join(dir, `${name}${cyWithExt}`))
+ const name = base.slice(0, base.indexOf('.'))
+
+ const finalExtension = filePath ? cyWithExt : specExt
+
+ return this.getFinalFileName(dir, name, finalExtension, path.join(dir, `${name}${finalExtension}`))
}
private async buildFileName () {
diff --git a/packages/data-context/src/codegen/templates/vue-component/vue-component.ejs b/packages/data-context/src/codegen/templates/vue-component/vue-component.ejs
index 48335fd9b5d4..1666e3edb517 100644
--- a/packages/data-context/src/codegen/templates/vue-component/vue-component.ejs
+++ b/packages/data-context/src/codegen/templates/vue-component/vue-component.ejs
@@ -2,7 +2,7 @@
fileName: <%= fileName %>
---
-import <%- componentName %> from "<%- componentPath %>"
+import <%- componentName %> from '<%- componentPath %>'
describe('<<%=componentName%> />', () => {
it('renders', () => {
diff --git a/packages/data-context/src/sources/ProjectDataSource.ts b/packages/data-context/src/sources/ProjectDataSource.ts
index 441dc4c17e2f..fab5fd97d693 100644
--- a/packages/data-context/src/sources/ProjectDataSource.ts
+++ b/packages/data-context/src/sources/ProjectDataSource.ts
@@ -21,6 +21,7 @@ import { toPosix } from '../util/file'
import type { FilePartsShape } from '@packages/graphql/src/schemaTypes/objectTypes/gql-FileParts'
import type { ProjectShape } from '../data'
import type { FindSpecs } from '../actions'
+import { getDefaultSpecFileName } from './migration/utils'
export type SpecWithRelativeRoot = FoundSpec & { relativeToCommonRoot: string }
@@ -134,7 +135,15 @@ export function getLongestCommonPrefixFromPaths (paths: string[]): string {
return lcp.slice(0, endIndex).join(path.sep)
}
-export function getPathFromSpecPattern (specPattern: string, testingType: TestingType, fileExtensionToUse?: 'js' | 'ts') {
+export function getPathFromSpecPattern ({
+ specPattern,
+ testingType,
+ fileExtensionToUse,
+ name = '' }:
+{ specPattern: string
+ testingType: TestingType
+ fileExtensionToUse?: 'js' | 'ts'
+ name?: string}) {
function replaceWildCard (s: string, fallback: string) {
return s.replace(/\*/g, fallback)
}
@@ -152,7 +161,7 @@ export function getPathFromSpecPattern (specPattern: string, testingType: Testin
if (dirname.startsWith('**')) dirname = dirname.replace('**', 'cypress')
const splittedDirname = dirname.split('/').filter((s) => s !== '**').map((x) => replaceWildCard(x, testingType)).join('/')
- const fileName = replaceWildCard(parsedGlob.path.filename, testingType === 'e2e' ? 'spec' : 'ComponentName')
+ const fileName = replaceWildCard(parsedGlob.path.filename, name ? name : testingType === 'e2e' ? 'spec' : 'ComponentName')
const extnameWithoutExt = parsedGlob.path.extname.replace(parsedGlob.path.ext, '')
|| `.cy.${fileExtensionToUse}`
@@ -386,49 +395,15 @@ export class ProjectDataSource {
}
async defaultSpecFileName (): Promise {
- const defaultFilename = `${this.ctx.coreData.currentTestingType === 'e2e' ? 'spec' : 'ComponentName'}.cy.${this.ctx.lifecycleManager.fileExtensionToUse}`
- const defaultPathname = path.join('cypress', this.ctx.coreData.currentTestingType ?? 'e2e', defaultFilename)
-
- if (!this.ctx.currentProject || !this.ctx.coreData.currentTestingType) {
- throw new Error('Failed to get default spec filename, missing currentProject/currentTestingType')
- }
-
- try {
- let specPatternSet: string | undefined
- const { specPattern = [] } = await this.ctx.project.specPatterns()
+ const { specPattern = [] } = await this.ctx.project.specPatterns()
- if (Array.isArray(specPattern)) {
- specPatternSet = specPattern[0]
- }
-
- // 1. If there is no spec pattern, use the default for this testing type.
- if (!specPatternSet) {
- return defaultPathname
- }
-
- // 2. If the spec pattern is the default spec pattern, return the default for this testing type.
- if (specPatternSet === defaultSpecPattern[this.ctx.coreData.currentTestingType]) {
- return defaultPathname
- }
-
- const pathFromSpecPattern = getPathFromSpecPattern(specPatternSet, this.ctx.coreData.currentTestingType, this.ctx.lifecycleManager.fileExtensionToUse)
- const filename = pathFromSpecPattern ? path.basename(pathFromSpecPattern) : defaultFilename
-
- // 3. If there are existing specs, return the longest common path prefix between them, if it is non-empty.
- const commonPrefixFromSpecs = getLongestCommonPrefixFromPaths(this.specs.map((spec) => spec.relative))
-
- if (commonPrefixFromSpecs) return path.join(commonPrefixFromSpecs, filename)
-
- // 4. Otherwise, return a path that fulfills the spec pattern.
- if (pathFromSpecPattern) return pathFromSpecPattern
-
- // 5. Return the default for this testing type if we cannot decide from the spec pattern.
- return defaultPathname
- } catch (err) {
- debug('Error intelligently detecting default filename, using safe default %o', err)
-
- return defaultPathname
- }
+ return getDefaultSpecFileName({
+ currentProject: this.ctx.currentProject,
+ testingType: this.ctx.coreData.currentTestingType,
+ fileExtensionToUse: this.ctx.lifecycleManager.fileExtensionToUse,
+ specs: this.specs,
+ specPattern,
+ })
}
async matchesSpecPattern (specFile: string): Promise {
diff --git a/packages/data-context/src/sources/migration/utils.ts b/packages/data-context/src/sources/migration/utils.ts
index 7afdfc79cf01..af89cbb39d65 100644
--- a/packages/data-context/src/sources/migration/utils.ts
+++ b/packages/data-context/src/sources/migration/utils.ts
@@ -1,4 +1,9 @@
+import { defaultSpecPattern } from '@packages/config'
+import type { TestingType, FoundSpec } from '@packages/types'
+import Debug from 'debug'
import _ from 'lodash'
+import path from 'path'
+import { getPathFromSpecPattern, getLongestCommonPrefixFromPaths } from '../ProjectDataSource'
export const isDefaultSupportFile = (supportFile: string) => {
if (_.isNil(supportFile) || !_.isBoolean(supportFile) && supportFile.match(/(^|\.+\/)cypress\/support($|\/index($|\.(ts|js|coffee)$))/)) {
@@ -7,3 +12,55 @@ export const isDefaultSupportFile = (supportFile: string) => {
return false
}
+
+export async function getDefaultSpecFileName (
+ { currentProject, testingType, fileExtensionToUse, specPattern, specs = [], name }:
+ { currentProject: string | null, testingType: TestingType | null, fileExtensionToUse: 'js' | 'ts', specPattern: string[], specs?: FoundSpec[], name?: string },
+): Promise {
+ const debug = Debug('cypress:data-context:sources:migration:utils')
+
+ const defaultFilename = `${name ? name : testingType === 'e2e' ? 'spec' : 'ComponentName'}.cy.${fileExtensionToUse}`
+ const defaultPathname = path.join('cypress', testingType ?? 'e2e', defaultFilename)
+
+ if (!currentProject || !testingType) {
+ debug('currentProject or testingType undefined. Error intelligently detecting default filename, using safe default %o', defaultPathname)
+
+ return defaultPathname
+ }
+
+ try {
+ let specPatternSet: string | undefined
+
+ if (Array.isArray(specPattern)) {
+ specPatternSet = specPattern[0]
+ }
+
+ // 1. If there is no spec pattern, use the default for this testing type.
+ if (!specPatternSet) {
+ return defaultPathname
+ }
+
+ // 2. If the spec pattern is the default spec pattern, return the default for this testing type.
+ if (specPatternSet === defaultSpecPattern[testingType]) {
+ return defaultPathname
+ }
+
+ const pathFromSpecPattern = getPathFromSpecPattern({ specPattern: specPatternSet, testingType, fileExtensionToUse, name })
+ const filename = pathFromSpecPattern ? path.basename(pathFromSpecPattern) : defaultFilename
+
+ // 3. If there are existing specs, return the longest common path prefix between them, if it is non-empty.
+ const commonPrefixFromSpecs = getLongestCommonPrefixFromPaths(specs.map((spec) => spec.relative))
+
+ if (commonPrefixFromSpecs) return path.join(commonPrefixFromSpecs, filename)
+
+ // 4. Otherwise, return a path that fulfills the spec pattern.
+ if (pathFromSpecPattern) return pathFromSpecPattern
+
+ // 5. Return the default for this testing type if we cannot decide from the spec pattern.
+ return defaultPathname
+ } catch (err) {
+ debug('Error intelligently detecting default filename, using safe default %o', err)
+
+ return defaultPathname
+ }
+}
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 dcf1b1a8aeba..b9d8c0bacfdf 100644
--- a/packages/data-context/test/unit/codegen/code-generator.spec.ts
+++ b/packages/data-context/test/unit/codegen/code-generator.spec.ts
@@ -11,6 +11,7 @@ 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 { defaultSpecPattern } from '@packages/config'
const tmpPath = path.join(__dirname, 'tmp/test-code-gen')
@@ -205,7 +206,7 @@ describe('code-generator', () => {
type: 'text',
status: 'add',
file: fileAbsolute,
- content: dedent`import ${codeGenArgs.componentName} from "${codeGenArgs.componentPath}"
+ content: dedent`import ${codeGenArgs.componentName} from '${codeGenArgs.componentPath}'
describe('<${codeGenArgs.componentName} />', () => {
it('renders', () => {
@@ -257,11 +258,12 @@ describe('code-generator', () => {
}
const newSpecCodeGenOptions = new SpecOptions({
+ currentProject: 'path/to/myProject',
codeGenPath: path.join(__dirname, 'files', 'react', 'Button.jsx'),
codeGenType: 'component',
- specFileExtension: '.cy',
framework: WIZARD_FRAMEWORKS[1],
isDefaultSpecPattern: true,
+ specPattern: [defaultSpecPattern.component],
})
let codeGenOptions = await newSpecCodeGenOptions.getCodeGenOptions()
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 a15c9543b66c..15fac55bc7ce 100644
--- a/packages/data-context/test/unit/codegen/spec-options.spec.ts
+++ b/packages/data-context/test/unit/codegen/spec-options.spec.ts
@@ -1,7 +1,9 @@
+import { defaultSpecPattern } from '@packages/config'
import { WIZARD_FRAMEWORKS } from '@packages/scaffold-config'
import { expect } from 'chai'
import fs from 'fs-extra'
import path from 'path'
+import sinon from 'sinon'
import { DataContext } from '../../../src'
import { SpecOptions, expectedSpecExtensions } from '../../../src/codegen/spec-options'
import { createTestDataContext } from '../helper'
@@ -30,9 +32,11 @@ describe('spec-options', () => {
for (const specExtension of expectedSpecExtensions) {
it(`generates options for names with extension ${specExtension}`, async () => {
const testSpecOptions = new SpecOptions({
+ currentProject: 'path/to/myProject',
codeGenPath: `${tmpPath}/TestName${specExtension}.js`,
codeGenType: 'e2e',
isDefaultSpecPattern: true,
+ specPattern: [defaultSpecPattern.e2e],
})
const result = await testSpecOptions.getCodeGenOptions()
@@ -44,9 +48,11 @@ describe('spec-options', () => {
it('generates options for file name without spec extension', async () => {
const testSpecOptions = new SpecOptions({
+ currentProject: 'path/to/myProject',
codeGenPath: `${tmpPath}/TestName.js`,
codeGenType: 'e2e',
isDefaultSpecPattern: true,
+ specPattern: [defaultSpecPattern.e2e],
})
const result = await testSpecOptions.getCodeGenOptions()
@@ -57,9 +63,11 @@ describe('spec-options', () => {
it('generates options for file name with multiple extensions', async () => {
const testSpecOptions = new SpecOptions({
+ currentProject: 'path/to/myProject',
codeGenPath: `${tmpPath}/TestName.foo.bar.js`,
codeGenType: 'e2e',
isDefaultSpecPattern: true,
+ specPattern: [defaultSpecPattern.e2e],
})
const result = await testSpecOptions.getCodeGenOptions()
@@ -67,18 +75,93 @@ describe('spec-options', () => {
expect(result.codeGenType).to.eq('e2e')
expect(result.fileName).to.eq(`TestName.foo.bar.js`)
})
+ })
- it('generates options with given codeGenType', async () => {
- const testSpecOptions = new SpecOptions({
- codeGenPath: `${tmpPath}/TestName.js`,
- codeGenType: 'component',
- isDefaultSpecPattern: true,
- framework: WIZARD_FRAMEWORKS[1],
+ context('create from Vue component', () => {
+ afterEach(function () {
+ sinon.restore()
+ })
+
+ context('default spec pattern', () => {
+ it('generates options for generating a Vue component spec', async () => {
+ const testSpecOptions = new SpecOptions({
+ currentProject: 'path/to/myProject',
+ codeGenPath: `${tmpPath}/MyComponent.vue`,
+ codeGenType: 'component',
+ isDefaultSpecPattern: true,
+ framework: WIZARD_FRAMEWORKS[1],
+ specPattern: [defaultSpecPattern.component],
+ })
+
+ const result = await testSpecOptions.getCodeGenOptions()
+
+ expect(result.codeGenType).to.eq('component')
+ expect(result.fileName).to.eq('MyComponent.cy.js')
})
- const result = await testSpecOptions.getCodeGenOptions()
+ it('creates copy file if spec already exists', async () => {
+ sinon.stub(fs, 'access').onFirstCall().resolves().onSecondCall().rejects()
+
+ const testSpecOptions = new SpecOptions({
+ currentProject: 'path/to/myProject',
+ codeGenPath: `${tmpPath}/MyComponent.vue`,
+ codeGenType: 'component',
+ isDefaultSpecPattern: true,
+ framework: WIZARD_FRAMEWORKS[1],
+ specPattern: [defaultSpecPattern.component],
+ })
+
+ const result = await testSpecOptions.getCodeGenOptions()
+
+ expect(result.codeGenType).to.eq('component')
+ expect(result.fileName).to.eq('MyComponent-copy-1.cy.js')
+ })
+ })
- expect(result.codeGenType).to.eq('component')
+ context('custom spec pattern', () => {
+ [{ testName: 'src/specs-folder/*.cy.{js,jsx}', componentPath: 'ComponentName.vue', specs: [], pattern: 'src/specs-folder/*.cy.{js,jsx}', expectedPath: 'src/specs-folder/ComponentName.cy.js' },
+ { testName: 'src/**/*.{spec,cy}.{js,jsx,ts,tsx}', componentPath: 'MyComponent.vue', specs: [], pattern: 'src/**/*.{spec,cy}.{js,jsx,ts,tsx}', expectedPath: 'src/MyComponent.spec.ts', isTypescriptComponent: true },
+ { testName: '**/*.test.js', componentPath: 'src/Foo.vue', specs: [], pattern: '**/*.test.js', expectedPath: 'cypress/Foo.test.js' },
+ { testName: 'src/**/*.js', componentPath: 'src/Foo.vue', specs: [], pattern: 'src/**/*.js', expectedPath: 'src/Foo.js' },
+ { testName: '**/*.js no specs', componentPath: 'src/Foo.vue', specs: [], pattern: '**/*.js', expectedPath: 'cypress/Foo.js' },
+ { testName: '**/*.js existing spec', componentPath: 'src/Foo.vue',
+ specs: [{ specType: 'component' as Cypress.CypressSpecType, name: 'src/Bar.cy.js', baseName: 'Bar.cy.js', fileName: 'Bar', relative: 'src/Bar.cy.js', absolute: `${tmpPath}/src/Bar.cy.js`, fileExtension: '.js', specFileExtension: '.cy.js' }],
+ pattern: '**/*.js', expectedPath: 'src/Foo.js' },
+ { testName: '**/*.js spec already exists', componentPath: 'src/Foo.vue',
+ specs: [{ specType: 'component' as Cypress.CypressSpecType, name: 'src/Foo.cy.js', baseName: 'Foo.cy.js', fileName: 'Foo', relative: 'src/Foo.cy.js', absolute: `${tmpPath}/src/Foo.cy.js`, fileExtension: '.js', specFileExtension: '.cy.js' }],
+ pattern: '**/*.cy.js', expectedPath: 'src/Foo-copy-1.cy.js', makeCopy: true }]
+ .forEach(({ testName, componentPath, specs, pattern, expectedPath, makeCopy, isTypescriptComponent }) => {
+ it(testName, async () => {
+ // This stub simulates the spec file already existing the first time we try, which should cause a copy to be created
+ if (makeCopy) {
+ sinon.stub(fs, 'access').onFirstCall().resolves().onSecondCall().rejects()
+ }
+
+ // This stub simulates that the component we are generating a spec from is using Typescript.
+ if (isTypescriptComponent) {
+ // @ts-ignore
+ sinon.stub(fs, 'readFile').resolves('lang="ts"')
+ }
+
+ const currentProject = 'path/to/myProject'
+ const specPattern = [pattern]
+
+ const testSpecOptions = new SpecOptions({
+ currentProject,
+ codeGenPath: `${tmpPath}/${componentPath}`,
+ codeGenType: 'component',
+ isDefaultSpecPattern: false,
+ framework: WIZARD_FRAMEWORKS[1],
+ specPattern,
+ specs,
+ })
+
+ const result = await testSpecOptions.getCodeGenOptions()
+
+ expect(result.codeGenType).to.eq('component')
+ expect(`${result.overrideCodeGenDir}/${result.fileName}`).to.eq(expectedPath)
+ })
+ })
})
})
@@ -86,9 +169,11 @@ describe('spec-options', () => {
for (const specExtension of expectedSpecExtensions) {
it(`generates options for file name with extension ${specExtension}`, async () => {
const testSpecOptions = new SpecOptions({
+ currentProject: 'path/to/myProject',
codeGenPath: `${tmpPath}/TestName${specExtension}.js`,
codeGenType: 'e2e',
isDefaultSpecPattern: true,
+ specPattern: [defaultSpecPattern.e2e],
})
await fs.outputFile(`${tmpPath}/TestName${specExtension}.js`, '// foo')
@@ -110,9 +195,11 @@ describe('spec-options', () => {
it('generates options for file name without spec extension', async () => {
const testSpecOptions = new SpecOptions({
+ currentProject: 'path/to/myProject',
codeGenPath: `${tmpPath}/TestName.js`,
codeGenType: 'e2e',
isDefaultSpecPattern: true,
+ specPattern: [defaultSpecPattern.e2e],
})
await fs.outputFile(`${tmpPath}/TestName.js`, '// foo')
@@ -133,9 +220,11 @@ describe('spec-options', () => {
it('generates options for file name with multiple extensions', async () => {
const testSpecOptions = new SpecOptions({
+ currentProject: 'path/to/myProject',
codeGenPath: `${tmpPath}/TestName.foo.bar.js`,
codeGenType: 'e2e',
isDefaultSpecPattern: true,
+ specPattern: [defaultSpecPattern.e2e],
})
await fs.outputFile(`${tmpPath}/TestName.foo.bar.js`, '// foo')
diff --git a/packages/data-context/test/unit/sources/ProjectDataSource.spec.ts b/packages/data-context/test/unit/sources/ProjectDataSource.spec.ts
index 0e903254dabc..ed7dfc16eb22 100644
--- a/packages/data-context/test/unit/sources/ProjectDataSource.spec.ts
+++ b/packages/data-context/test/unit/sources/ProjectDataSource.spec.ts
@@ -300,31 +300,31 @@ describe('getPathFromSpecPattern', () => {
context('dirname', () => {
it('returns pattern without change if it is do not a glob', () => {
const specPattern = 'cypress/e2e/foo.spec.ts'
- const defaultFileName = getPathFromSpecPattern(specPattern, 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern, testingType: 'e2e' })
expect(defaultFileName).to.eq(specPattern)
})
it('remove ** from glob if it is not in the beginning', () => {
- const defaultFileName = getPathFromSpecPattern('cypress/**/foo.spec.ts', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'cypress/**/foo.spec.ts', testingType: 'e2e' })
expect(defaultFileName).to.eq('cypress/foo.spec.ts')
})
it('replace ** for cypress if it starts with **', () => {
- const defaultFileName = getPathFromSpecPattern('**/e2e/foo.spec.ts', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: '**/e2e/foo.spec.ts', testingType: 'e2e' })
expect(defaultFileName).to.eq('cypress/e2e/foo.spec.ts')
})
it('replace ** for cypress if it starts with ** and omit extra **', () => {
- const defaultFileName = getPathFromSpecPattern('**/**/foo.spec.ts', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: '**/**/foo.spec.ts', testingType: 'e2e' })
expect(defaultFileName).to.eq('cypress/foo.spec.ts')
})
it('selects first option if there are multiples possibilities of values', () => {
- const defaultFileName = getPathFromSpecPattern('{cypress,tests}/{integration,e2e}/foo.spec.ts', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: '{cypress,tests}/{integration,e2e}/foo.spec.ts', testingType: 'e2e' })
expect(defaultFileName).to.eq('cypress/integration/foo.spec.ts')
})
@@ -332,13 +332,13 @@ describe('getPathFromSpecPattern', () => {
context('filename', () => {
it('replace * for filename', () => {
- const defaultFileName = getPathFromSpecPattern('cypress/e2e/*.spec.ts', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'cypress/e2e/*.spec.ts', testingType: 'e2e' })
expect(defaultFileName).to.eq('cypress/e2e/spec.spec.ts')
})
it('selects first option if there are multiples possibilities of values', () => {
- const defaultFileName = getPathFromSpecPattern('cypress/e2e/{foo,filename}.spec.ts', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'cypress/e2e/{foo,filename}.spec.ts', testingType: 'e2e' })
expect(defaultFileName).to.eq('cypress/e2e/foo.spec.ts')
})
@@ -346,13 +346,13 @@ describe('getPathFromSpecPattern', () => {
context('test extension', () => {
it('replace * for filename', () => {
- const defaultFileName = getPathFromSpecPattern('cypress/e2e/filename.*.ts', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'cypress/e2e/filename.*.ts', testingType: 'e2e' })
expect(defaultFileName).to.eq('cypress/e2e/filename.cy.ts')
})
it('selects first option if there are multiples possibilities of values', () => {
- const defaultFileName = getPathFromSpecPattern('cypress/e2e/filename.{spec,cy}.ts', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'cypress/e2e/filename.{spec,cy}.ts', testingType: 'e2e' })
expect(defaultFileName).to.eq('cypress/e2e/filename.spec.ts')
})
@@ -360,25 +360,25 @@ describe('getPathFromSpecPattern', () => {
context('lang extension', () => {
it('if project use TS, set TS as extension if it exists in the glob', () => {
- const defaultFileName = getPathFromSpecPattern('cypress/e2e/filename.cy.ts', 'e2e', 'ts')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'cypress/e2e/filename.cy.ts', testingType: 'e2e', fileExtensionToUse: 'ts' })
expect(defaultFileName).to.eq('cypress/e2e/filename.cy.ts')
})
it('if project use TS, set TS as extension if it exists in the options of extensions', () => {
- const defaultFileName = getPathFromSpecPattern('cypress/e2e/filename.cy.{js,ts,tsx}', 'e2e', 'ts')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'cypress/e2e/filename.cy.{js,ts,tsx}', testingType: 'e2e', fileExtensionToUse: 'ts' })
expect(defaultFileName).to.eq('cypress/e2e/filename.cy.ts')
})
it('if project use TS, do not set TS as extension if it do not exists in the options of extensions', () => {
- const defaultFileName = getPathFromSpecPattern('cypress/e2e/filename.cy.{js,jsx}', 'e2e', 'ts')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'cypress/e2e/filename.cy.{js,jsx}', testingType: 'e2e', fileExtensionToUse: 'ts' })
expect(defaultFileName).to.eq('cypress/e2e/filename.cy.js')
})
it('selects first option if there are multiples possibilities of values', () => {
- const defaultFileName = getPathFromSpecPattern('cypress/e2e/filename.cy.{ts,js}', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'cypress/e2e/filename.cy.{ts,js}', testingType: 'e2e' })
expect(defaultFileName).to.eq('cypress/e2e/filename.cy.ts')
})
@@ -386,43 +386,43 @@ describe('getPathFromSpecPattern', () => {
context('extra cases', () => {
it('creates specName for tests/*.js', () => {
- const defaultFileName = getPathFromSpecPattern('tests/*.js', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'tests/*.js', testingType: 'e2e' })
expect(defaultFileName).to.eq('tests/spec.js')
})
it('creates specName for src/*-test.js', () => {
- const defaultFileName = getPathFromSpecPattern('src/*-test.js', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'src/*-test.js', testingType: 'e2e' })
expect(defaultFileName).to.eq('src/spec-test.js')
})
it('creates specName for src/*.foo.bar.js', () => {
- const defaultFileName = getPathFromSpecPattern('src/*.foo.bar.js', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'src/*.foo.bar.js', testingType: 'e2e' })
expect(defaultFileName).to.eq('src/spec.foo.bar.js')
})
it('creates specName for src/prefix.*.test.js', () => {
- const defaultFileName = getPathFromSpecPattern('src/prefix.*.test.js', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'src/prefix.*.test.js', testingType: 'e2e' })
expect(defaultFileName).to.eq('src/prefix.cy.test.js')
})
it('creates specName for src/*/*.test.js', () => {
- const defaultFileName = getPathFromSpecPattern('src/*/*.test.js', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'src/*/*.test.js', testingType: 'e2e' })
expect(defaultFileName).to.eq('src/e2e/spec.test.js')
})
it('creates specName for src-*/**/*.test.js', () => {
- const defaultFileName = getPathFromSpecPattern('src-*/**/*.test.js', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'src-*/**/*.test.js', testingType: 'e2e' })
expect(defaultFileName).to.eq('src-e2e/spec.test.js')
})
it('creates specName for src/*.test.(js|jsx)', () => {
- const defaultFileName = getPathFromSpecPattern('src/*.test.(js|jsx)', 'component')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'src/*.test.(js|jsx)', testingType: 'component' })
const possiblesFileNames = ['src/ComponentName.test.jsx', 'src/ComponentName.test.js']
@@ -430,7 +430,7 @@ describe('getPathFromSpecPattern', () => {
})
it('creates specName for (src|components)/**/*.test.js', () => {
- const defaultFileName = getPathFromSpecPattern('(src|components)/**/*.test.js', 'component')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: '(src|components)/**/*.test.js', testingType: 'component' })
const possiblesFileNames = ['src/ComponentName.test.js', 'components/ComponentName.test.js']
@@ -438,13 +438,13 @@ describe('getPathFromSpecPattern', () => {
})
it('creates specName for e2e/**/*.cy.{js,jsx,ts,tsx}', () => {
- const defaultFileName = getPathFromSpecPattern('e2e/**/*.cy.{js,jsx,ts,tsx}', 'e2e')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'e2e/**/*.cy.{js,jsx,ts,tsx}', testingType: 'e2e' })
expect(defaultFileName).to.eq('e2e/spec.cy.js')
})
it('creates specName for cypress/component-tests/**/*', () => {
- const defaultFileName = getPathFromSpecPattern('cypress/component-tests/**/*', 'component', 'ts')
+ const defaultFileName = getPathFromSpecPattern({ specPattern: 'cypress/component-tests/**/*', testingType: 'component', fileExtensionToUse: 'ts' })
expect(defaultFileName).to.eq('cypress/component-tests/ComponentName.cy.ts')
})
@@ -772,6 +772,8 @@ describe('ProjectDataSource', () => {
context('#defaultSpecFilename', () => {
it('yields default if no spec pattern is set', async () => {
+ sinon.stub(ctx.project, 'specPatterns').resolves({ specPattern: [] })
+
const defaultSpecFileName = await ctx.project.defaultSpecFileName()
expect(defaultSpecFileName).to.equal('cypress/e2e/spec.cy.js')
diff --git a/packages/graphql/schemas/schema.graphql b/packages/graphql/schemas/schema.graphql
index c6f4110370af..613eebaa0b63 100644
--- a/packages/graphql/schemas/schema.graphql
+++ b/packages/graphql/schemas/schema.graphql
@@ -1211,7 +1211,7 @@ type Mutation {
focusActiveBrowserWindow: Boolean!
"""Generate spec from source"""
- generateSpecFromSource(codeGenCandidate: String!, erroredCodegenCandidate: String, type: CodeGenType!): GenerateSpecResponse
+ generateSpecFromSource(codeGenCandidate: String!, type: CodeGenType!): GenerateSpecResponse
internal_clearAllProjectPreferencesCache: Boolean
internal_clearLatestProjectCache: Boolean
internal_clearProjectPreferencesCache(projectTitle: String!): Boolean
diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts
index 3baf389f8087..8c3f4a9770f2 100644
--- a/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts
+++ b/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts
@@ -246,10 +246,9 @@ export const mutation = mutationType({
args: {
codeGenCandidate: nonNull(stringArg()),
type: nonNull(CodeGenTypeEnum),
- erroredCodegenCandidate: stringArg(),
},
resolve: (_, args, ctx) => {
- return ctx.actions.project.codeGenSpec(args.codeGenCandidate, args.type, args.erroredCodegenCandidate)
+ return ctx.actions.project.codeGenSpec(args.codeGenCandidate, args.type)
},
})
diff --git a/system-tests/projects/no-specs-vue-2/cypress-custom-spec-pattern.config.js b/system-tests/projects/no-specs-vue-2/cypress-custom-spec-pattern.config.js
new file mode 100644
index 000000000000..5910e12bd83b
--- /dev/null
+++ b/system-tests/projects/no-specs-vue-2/cypress-custom-spec-pattern.config.js
@@ -0,0 +1,9 @@
+module.exports = {
+ component: {
+ specPattern: 'src/specs-folder/*.cy.{js,jsx}',
+ devServer: {
+ framework: 'vue-cli',
+ bundler: 'webpack'
+ }
+ }
+}