feat: CT stack traces (#23916)

Co-authored-by: Zachary Williams <zachjw34@gmail.com>
Co-authored-by: astone123 <adams@cypress.io>
Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com>
This commit is contained in:
Mike Plummer
2022-09-26 17:31:46 -05:00
committed by GitHub
parent 3aad5a03e9
commit bf590eba3f
152 changed files with 5950 additions and 137 deletions

View File

@@ -63,7 +63,7 @@ windowsWorkflowFilters: &windows-workflow-filters
or:
- equal: [ develop, << pipeline.git.branch >> ]
- equal: [ linux-arm64, << pipeline.git.branch >> ]
- equal: [ 'lmiller/fixing-windows-ci', << pipeline.git.branch >> ]
- equal: [ 'mikep/21720-ct-stack-traces', << pipeline.git.branch >> ]
- matches:
pattern: "-release$"
value: << pipeline.git.branch >>

View File

@@ -24,12 +24,22 @@ if (supportFile) {
// We need a slash before /cypress/supportFile.js, this happens by default
// with the current string replacement logic.
importsToLoad.push(() => import(`${devServerPublicPathRoute}${supportRelativeToProjectRoot}`))
importsToLoad.push({
load: () => import(`${devServerPublicPathRoute}${supportRelativeToProjectRoot}`),
absolute: supportFile,
relative: supportRelativeToProjectRoot,
relativeUrl: `${devServerPublicPathRoute}${supportRelativeToProjectRoot}`,
})
}
/* Spec file import logic */
// We need a slash before /src/my-spec.js, this does not happen by default.
importsToLoad.push(() => import(`${devServerPublicPathRoute}/${CypressInstance.spec.relative}`))
importsToLoad.push({
load: () => import(`${devServerPublicPathRoute}/${CypressInstance.spec.relative}`),
absolute: CypressInstance.spec.absolute,
relative: CypressInstance.spec.relative,
relativeUrl: `${devServerPublicPathRoute}/${CypressInstance.spec.relative}`,
})
if (!CypressInstance) {
throw new Error('Tests cannot run without a reference to Cypress!')

View File

@@ -57,6 +57,7 @@ for (const project of VITE_REACT) {
cy.contains('MissingReactInSpec.cy.jsx').click()
cy.waitForSpecToFinish()
cy.get('.failed > .num').should('contain', 1)
cy.get('.test-err-code-frame').should('be.visible')
cy.withCtx(async (ctx) => {
await ctx.actions.file.writeFileInProject(`src/MissingReactInSpec.cy.jsx`,
await ctx.file.readFileInProject('src/App.cy.jsx'))

View File

@@ -105,6 +105,27 @@ export const Cypress = (
return res.end(transformedIndexHtml)
})
},
transform (code, id, options?) {
try {
if (/\.js$/i.test(id) && !/\/\/# sourceMappingURL=/i.test(code)) {
// The Vite dev server and plugins automatically transpile TS and JSX files, which results in sourcemaps being generated
// and included in output. However, Vite serves up `esnext`-compliant Javascript which means many JS files won't be
// transpiled and won't supply a sourcemap - this prevents Cypress from providing codeFrames in the event of an error.
//
// A sourcemap is generated by Vite for JS files (just not included) which is in effect an "identity" sourcemap mapping
// 1-to-1 to the output file. We can grab this and pass it along as a sourcemap we want Vite to embed into the output,
// giving Cypress a sourcemap to use for codeFrame lookups.
// @see https://rollupjs.org/guide/en/#thisgetcombinedsourcemap
return {
code,
map: this.getCombinedSourcemap(),
}
}
} catch (_err) {
debug('Failed to propagate sourcemap for %s: %o', id, _err)
}
},
handleHotUpdate: ({ server, file }) => {
debug('handleHotUpdate - file', file)

View File

@@ -10,17 +10,18 @@ exports['makeWebpackConfig ignores userland webpack `output.publicPath` and `dev
"overlay": false
}
},
"mode": "development",
"optimization": {
"emitOnErrors": true,
"splitChunks": {
"chunks": "all"
}
},
"mode": "development",
"plugins": [
"HtmlWebpackPlugin",
"CypressCTWebpackPlugin"
]
],
"devtool": "inline-source-map"
}
exports['makeWebpackConfig ignores userland webpack `output.publicPath` and `devServer.overlay` with webpack-dev-server v3 1'] = {
@@ -32,15 +33,16 @@ exports['makeWebpackConfig ignores userland webpack `output.publicPath` and `dev
"progress": true,
"overlay": false
},
"mode": "development",
"optimization": {
"noEmitOnErrors": false,
"splitChunks": {
"chunks": "all"
}
},
"mode": "development",
"plugins": [
"HtmlWebpackPlugin",
"CypressCTWebpackPlugin"
]
],
"devtool": "inline-source-map"
}

View File

@@ -73,6 +73,7 @@ for (const project of WEBPACK_REACT) {
// The test should fail and the stack trace should appear in the command log
cy.waitForSpecToFinish({ failCount: 1 })
cy.contains('The following error originated from your test code, not from Cypress.').should('exist')
cy.get('.test-err-code-frame').should('be.visible')
})
// TODO: fix flaky test https://github.com/cypress-io/cypress/issues/23455

View File

@@ -38,6 +38,7 @@ for (const project of WEBPACK_REACT) {
})
cy.waitForSpecToFinish({ failCount: 1 })
cy.get('.test-err-code-frame').should('be.visible')
cy.withCtx(async (ctx) => {
await ctx.actions.file.writeFileInProject(

View File

@@ -40,6 +40,7 @@ for (const project of WEBPACK_REACT) {
})
cy.waitForSpecToFinish({ failCount: 1 })
cy.get('.test-err-code-frame').should('be.visible')
cy.withCtx(async (ctx) => {
const indexTestPath = ctx.path.join('pages', 'index.cy.js')

View File

@@ -35,6 +35,7 @@ for (const project of PROJECTS) {
})
cy.waitForSpecToFinish({ failCount: 1 })
cy.get('.test-err-code-frame').should('be.visible')
cy.withCtx(async (ctx) => {
const tutorialCyPath = ctx.path.join('components', 'Tutorial.cy.js')

View File

@@ -24,8 +24,7 @@ for (const project of WEBPACK_REACT) {
it('should mount a passing test', () => {
cy.visitApp()
cy.contains('App.cy.jsx').click()
cy.waitForSpecToFinish()
cy.get('.passed > .num').should('contain', 1)
cy.waitForSpecToFinish({ passCount: 1 })
})
it('MissingReact: should fail, rerun, succeed', () => {
@@ -36,15 +35,15 @@ for (const project of WEBPACK_REACT) {
cy.visitApp()
cy.contains('MissingReact.cy.jsx').click()
cy.waitForSpecToFinish()
cy.get('.failed > .num').should('contain', 1)
cy.waitForSpecToFinish({ failCount: 1 })
cy.get('.test-err-code-frame').should('be.visible')
cy.withCtx(async (ctx) => {
await ctx.actions.file.writeFileInProject(`src/MissingReact.jsx`,
`import React from 'react';
${await ctx.file.readFileInProject('src/MissingReact.jsx')}`)
})
cy.get('.passed > .num').should('contain', 1)
cy.waitForSpecToFinish({ passCount: 1 })
})
it('MissingReactInSpec: should fail, rerun, succeed', () => {
@@ -55,14 +54,14 @@ for (const project of WEBPACK_REACT) {
cy.visitApp()
cy.contains('MissingReactInSpec.cy.jsx').click()
cy.waitForSpecToFinish()
cy.get('.failed > .num').should('contain', 1)
cy.waitForSpecToFinish({ failCount: 1 })
cy.get('.test-err-code-frame').should('be.visible')
cy.withCtx(async (ctx) => {
await ctx.actions.file.writeFileInProject(`src/MissingReactInSpec.cy.jsx`,
await ctx.file.readFileInProject('src/App.cy.jsx'))
})
cy.get('.passed > .num').should('contain', 1)
cy.waitForSpecToFinish({ passCount: 1 })
})
it('AppCompilationError: should fail with uncaught exception error', () => {
@@ -73,8 +72,7 @@ for (const project of WEBPACK_REACT) {
cy.visitApp()
cy.contains('AppCompilationError.cy.jsx').click()
cy.waitForSpecToFinish()
cy.get('.failed > .num').should('contain', 1)
cy.waitForSpecToFinish({ failCount: 1 })
cy.contains('An uncaught error was detected outside of a test')
cy.contains('The following error originated from your test code, not from Cypress.')
@@ -86,8 +84,7 @@ for (const project of WEBPACK_REACT) {
)
})
cy.waitForSpecToFinish()
cy.get('.passed > .num').should('contain', 1)
cy.waitForSpecToFinish({ passCount: 1 })
const appCompilationErrorSpec = dedent`
import React from 'react'
@@ -109,8 +106,7 @@ for (const project of WEBPACK_REACT) {
)
}, { appCompilationErrorSpec })
cy.waitForSpecToFinish()
cy.get('.failed > .num').should('contain', 1)
cy.waitForSpecToFinish({ failCount: 1 })
cy.contains('An uncaught error was detected outside of a test')
cy.contains('The following error originated from your test code, not from Cypress.')
})
@@ -121,7 +117,7 @@ for (const project of WEBPACK_REACT) {
// 1. assert spec executes successfully
cy.contains('App.cy.jsx').click()
cy.get('.passed > .num').should('contain', 1)
cy.waitForSpecToFinish({ passCount: 1 })
// 2. remove file from file system
cy.withCtx(async (ctx) => {
@@ -143,7 +139,7 @@ for (const project of WEBPACK_REACT) {
})
// 5. assert recreated spec executes successfully
cy.get('.passed > .num').should('contain', 1)
cy.waitForSpecToFinish({ passCount: 1 })
})
})
}

View File

@@ -28,6 +28,36 @@ for (const project of PROJECTS) {
})
})
it('should live-reload on src changes', () => {
cy.visitApp()
cy.contains('HelloWorld.cy.js').click()
cy.waitForSpecToFinish({ passCount: 1 })
cy.withCtx(async (ctx) => {
const helloWorldVuePath = ctx.path.join('src', 'components', 'HelloWorld.vue')
await ctx.actions.file.writeFileInProject(
helloWorldVuePath,
(await ctx.file.readFileInProject(helloWorldVuePath)).replace('{{ msg }}', ''),
)
})
cy.waitForSpecToFinish({ failCount: 1 })
cy.get('.test-err-code-frame').should('be.visible')
cy.withCtx(async (ctx) => {
const helloWorldVuePath = ctx.path.join('src', 'components', 'HelloWorld.vue')
await ctx.actions.file.writeFileInProject(
helloWorldVuePath,
(await ctx.file.readFileInProject(helloWorldVuePath)).replace('<h1></h1>', '<h1>{{ msg }}</h1>'),
)
})
cy.waitForSpecToFinish({ passCount: 1 })
})
it('should show compilation errors on src changes', () => {
cy.visitApp()

View File

@@ -2,6 +2,7 @@ import * as fs from 'fs-extra'
import { tmpdir } from 'os'
import * as path from 'path'
import { pathToFileURL } from 'url'
import type { Configuration } from 'webpack'
import type { PresetHandlerResult, WebpackDevServerConfig } from '../devServer'
import { sourceDefaultWebpackDependencies } from './sourceRelativeWebpackModules'
@@ -253,8 +254,16 @@ async function getAngularCliWebpackConfig (devServerConfig: AngularWebpackDevSer
return config
}
function removeSourceMapPlugin (config: Configuration) {
config.plugins = config.plugins?.filter((plugin) => {
return plugin?.constructor?.name !== 'SourceMapDevToolPlugin'
})
}
export async function angularHandler (devServerConfig: AngularWebpackDevServerConfig): Promise<PresetHandlerResult> {
const webpackConfig = await getAngularCliWebpackConfig(devServerConfig)
removeSourceMapPlugin(webpackConfig)
return { frameworkConfig: webpackConfig, sourceWebpackModulesResult: sourceDefaultWebpackDependencies(devServerConfig) }
}

View File

@@ -20,7 +20,9 @@ const makeImport = (file: Cypress.Cypress['spec'], filename: string, chunkName:
return `"${filename}": {
shouldLoad: () => document.location.pathname.includes("${encodeURI(file.absolute)}"),
load: () => import("${file.absolute}" ${magicComments}),
chunkName: "${chunkName}",
absolute: "${file.absolute.split(path.sep).join(path.posix.sep)}",
relative: "${file.relative.split(path.sep).join(path.posix.sep)}",
relativeUrl: "/__cypress/src/${chunkName}.js",
}`
}
@@ -60,9 +62,8 @@ export default function loader (this: unknown) {
const { files, projectRoot, supportFile } = ctx._cypress
const supportFileAbsolutePath = supportFile ? JSON.stringify(path.resolve(projectRoot, supportFile)) : undefined
return `
var loadSupportFile = ${supportFile ? `() => import(${supportFileAbsolutePath})` : `() => Promise.resolve()`}
const supportFileRelativePath = supportFile ? JSON.stringify(path.relative(projectRoot, supportFileAbsolutePath || '')) : undefined
const result = `
var allTheSpecs = ${buildSpecs(projectRoot, files)};
var { init } = require(${JSON.stringify(require.resolve('./aut-runner'))})
@@ -70,11 +71,23 @@ export default function loader (this: unknown) {
var scriptLoaders = Object.values(allTheSpecs).reduce(
(accSpecLoaders, specLoader) => {
if (specLoader.shouldLoad()) {
accSpecLoaders.push(specLoader.load)
accSpecLoaders.push(specLoader)
}
return accSpecLoaders
}, [loadSupportFile])
}, [])
if (${!!supportFile}) {
var supportFile = {
absolute: ${supportFileAbsolutePath},
relative: ${supportFileRelativePath},
relativeUrl: "/__cypress/src/cypress-support-file.js",
load: () => import(${supportFileAbsolutePath} /* webpackChunkName: "cypress-support-file" */),
}
scriptLoaders.unshift(supportFile)
}
init(scriptLoaders)
`
return result
}

View File

@@ -51,6 +51,7 @@ export function makeDefaultWebpackConfig (
...(config.devServerConfig.framework === 'angular' ? { scriptLoading: 'module' } : {}),
}),
],
devtool: 'inline-source-map',
} as any
if (config.sourceWebpackModulesResult.webpackDevServer.majorVersion === 4) {

View File

@@ -166,6 +166,11 @@ export async function makeWebpackConfig (
dynamicWebpackConfig,
)
// Some frameworks (like Next.js) change this value which changes the path we would need to use to fetch our spec.
// (eg, http://localhost:xxxx/<dev-server-public-path>/static/chunks/spec-<x>.js). Deleting this key to normalize
// the spec URL to `*/spec-<x>.js` which we need to know up-front so we can fetch the sourcemaps.
delete mergedConfig.output?.chunkFilename
// Angular loads global styles and polyfills via script injection in the index.html
if (framework === 'angular') {
mergedConfig.entry = {

View File

@@ -31,6 +31,7 @@ describe('makeWebpackConfig', () => {
optimization: {
noEmitOnErrors: true, // This will be overridden by makeWebpackConfig.ts
},
devtool: 'eval', // This will be overridden by makeWebpackConfig.ts
},
devServerEvents: new EventEmitter(),
}
@@ -77,6 +78,7 @@ describe('makeWebpackConfig', () => {
optimization: {
emitOnErrors: false, // This will be overridden by makeWebpackConfig.ts
},
devtool: 'eval', // This will be overridden by makeWebpackConfig.ts
},
devServerEvents: new EventEmitter(),
}

View File

@@ -48,6 +48,7 @@ export default defineConfig({
// Delete this as we only want to honor it on parent Cypress when doing E2E Cypress in Cypress testing
delete process.env.HTTP_PROXY_TARGET_FOR_ORIGIN_REQUESTS
process.env.CYPRESS_INTERNAL_E2E_TESTING_SELF = 'true'
process.env.CYPRESS_INTERNAL_VITE_OPEN_MODE_TESTING = 'true'
// process.env.DEBUG = '*'
const { e2ePluginSetup } = require('@packages/frontend-shared/cypress/e2e/e2ePluginSetup')

View File

@@ -82,7 +82,7 @@ function validateCreateFromComponentCard (beforeEachFn: () => void, expectedSpec
.should('have.attr', 'href', `#/specs/runner?file=${expectedSpecPath}`).click()
})
cy.findByText('<HelloWorld ... />').should('be.visible')
cy.findByText('<HelloWorld ... />', { timeout: 10000 }).should('be.visible')
})
}

View File

@@ -0,0 +1,397 @@
import type { ProjectFixtureDir } from '@tooling/system-tests/'
import { createVerify } from './support/verify-failures'
type VerifyFunc = (specTitle: string, verifyOptions: any) => void
type Options = {
projectName: ProjectFixtureDir
configFile: string
filePath: string
failCount: number
passCount?: number
}
Cypress.on('uncaught:exception', () => false)
/**
* Navigates to desired error spec file within Cypress app and waits for completion.
* Returns scoped verify function to aid inner spec validation.
*/
function loadErrorSpec (options: Options): VerifyFunc {
const { projectName, filePath, failCount, passCount = '--', configFile } = options
cy.openProject(projectName, ['--config-file', configFile])
cy.startAppServer('component')
cy.visitApp(`specs/runner?file=${filePath}`)
cy.findByLabelText('Stats', { timeout: 30000 }).within(() => {
cy.get('.passed .num', { timeout: 30000 }).should('have.text', `${passCount}`)
cy.get('.failed .num', { timeout: 30000 }).should('have.text', `${failCount}`)
})
// Return scoped verify function with spec options baked in
return createVerify({ fileName: Cypress._.last(filePath.split('/')), hasPreferredIde: false, mode: 'component' })
}
const reactVersions = [17, 18] as const
reactVersions.forEach((reactVersion) => {
describe(`React ${reactVersion}`, {
viewportHeight: 768,
viewportWidth: 1024,
// Limiting tests kept in memory due to large memory cost
// of nested spec snapshots
numTestsKeptInMemory: 1,
}, () => {
beforeEach(() => {
cy.scaffoldProject(`react${reactVersion}`)
})
it('error conditions', () => {
const verify = loadErrorSpec({
projectName: `react${reactVersion}`,
configFile: 'cypress-vite.config.ts',
filePath: 'src/Errors.cy.jsx',
failCount: 4,
})
verify('error on mount', {
line: 6,
column: 33,
uncaught: true,
uncaughtMessage: 'mount error',
message: [
'The following error originated from your test code',
'mount error',
],
codeFrameText: 'Errors.cy.jsx',
})
verify('sync error', {
line: 11,
column: 34,
uncaught: true,
uncaughtMessage: 'sync error',
message: [
'The following error originated from your test code',
'sync error',
],
codeFrameText: 'Errors.cy.jsx',
})
verify('async error', {
line: 18,
column: 38,
uncaught: true,
uncaughtMessage: 'async error',
message: [
'The following error originated from your test code',
'async error',
],
codeFrameText: 'Errors.cy.jsx',
})
verify('command failure', {
line: 43,
column: 8,
command: 'get',
message: [
'Timed out retrying',
'element-that-does-not-exist',
],
})
})
})
})
describe('Next.js', {
viewportHeight: 768,
viewportWidth: 1024,
// Limiting tests kept in memory due to large memory cost
// of nested spec snapshots
numTestsKeptInMemory: 1,
}, () => {
beforeEach(() => {
cy.scaffoldProject('next-12')
})
it('error conditions', () => {
const verify = loadErrorSpec({
projectName: 'next-12',
configFile: 'cypress.config.js',
filePath: 'cypress/Errors.cy.jsx',
failCount: 4,
})
verify('error on mount', {
line: 7,
column: 33,
uncaught: true,
uncaughtMessage: 'mount error',
message: [
'The following error originated from your test code',
'mount error',
],
codeFrameText: 'Errors.cy.jsx',
})
verify('sync error', {
line: 12,
column: 34,
uncaught: true,
uncaughtMessage: 'sync error',
message: [
'The following error originated from your test code',
'sync error',
],
codeFrameText: 'Errors.cy.jsx',
})
verify('async error', {
line: 19,
column: 38,
uncaught: true,
uncaughtMessage: 'async error',
message: [
'The following error originated from your test code',
'async error',
],
codeFrameText: 'Errors.cy.jsx',
})
verify('command failure', {
line: 44,
column: 8,
command: 'get',
message: [
'Timed out retrying',
'element-that-does-not-exist',
],
})
})
})
describe('Vue', {
viewportHeight: 768,
viewportWidth: 1024,
// Limiting tests kept in memory due to large memory cost
// of nested spec snapshots
numTestsKeptInMemory: 1,
}, () => {
beforeEach(() => {
cy.scaffoldProject('vuecli5-vue3')
})
it('error conditions', () => {
const verify = loadErrorSpec({
projectName: 'vuecli5-vue3',
configFile: 'cypress.config.ts',
filePath: 'src/components/Errors.cy.js',
failCount: 4,
})
verify('error on mount', {
fileName: 'Errors.vue',
line: 19,
column: 16,
message: [
'mount error',
],
codeFrameText: 'Errors.vue',
})
verify('sync error', {
fileName: 'Errors.vue',
line: 24,
column: 16,
uncaught: true,
uncaughtMessage: 'sync error',
message: [
'The following error originated from your test code',
'sync error',
],
codeFrameText: 'Errors.vue',
})
verify('async error', {
fileName: 'Errors.vue',
line: 28,
column: 18,
uncaught: true,
uncaughtMessage: 'async error',
message: [
'The following error originated from your test code',
'async error',
],
codeFrameText: 'Errors.vue',
})
verify('command failure', {
command: 'get',
message: [
'Timed out retrying',
'element-that-does-not-exist',
],
codeFrameRegex: /Errors\.cy\.js:26/,
stackRegex: /Errors\.cy\.js:26/,
})
})
})
describe('Nuxt', {
viewportHeight: 768,
viewportWidth: 1024,
// Limiting tests kept in memory due to large memory cost
// of nested spec snapshots
numTestsKeptInMemory: 1,
}, () => {
beforeEach(() => {
cy.scaffoldProject('nuxtjs-vue2-configured')
})
it('error conditions', () => {
const verify = loadErrorSpec({
projectName: 'nuxtjs-vue2-configured',
configFile: 'cypress.config.js',
filePath: 'components/Errors.cy.js',
failCount: 3,
})
verify('error on mount', {
fileName: 'Errors.vue',
line: 19,
message: [
'mount error',
],
stackRegex: /Errors\.vue:19/,
codeFrameText: 'Errors.vue',
})
verify('async error', {
fileName: 'Errors.vue',
line: 28,
uncaught: true,
uncaughtMessage: 'async error',
message: [
'The following error originated from your test code',
'async error',
],
stackRegex: /Errors\.vue:28/,
codeFrameText: 'Errors.vue',
})
verify('command failure', {
command: 'get',
message: [
'Timed out retrying',
'element-that-does-not-exist',
],
codeFrameRegex: /Errors\.cy\.js:26/,
stackRegex: /Errors\.cy\.js:26/,
})
})
})
// TODO: Svelte sourcemaps are generated but are not working properly on Webpack or Vite
// https://github.com/cypress-io/cypress/issues/23918
describe.skip('Svelte', {
viewportHeight: 768,
viewportWidth: 1024,
// Limiting tests kept in memory due to large memory cost
// of nested spec snapshots
numTestsKeptInMemory: 1,
}, () => {
beforeEach(() => {
cy.scaffoldProject('svelte-webpack')
})
it('error conditions', () => {
const verify = loadErrorSpec({
projectName: 'svelte-webpack',
configFile: 'cypress.config.js',
filePath: 'src/errors.cy.js',
failCount: 4,
})
verify('error on mount', {
message: [
'mount error',
],
hasCodeFrame: false,
codeFrameRegex: /spec-1.js:70:9/,
stackRegex: /spec-1.js:70:9/,
})
verify('sync error', {
// fileName: 'Errors.vue',
line: 24,
column: 16,
uncaught: true,
message: [
'The following error originated from your test code',
'sync error',
],
})
verify('async error', {
// fileName: 'Errors.vue',
line: 28,
column: 18,
uncaught: true,
message: [
'The following error originated from your test code',
'async error',
],
// codeFrameText: 'Errors.vue',
})
verify('command failure', {
command: 'get',
message: [
'Timed out retrying',
'element-that-does-not-exist',
],
codeFrameRegex: /errors\.cy\.js:26/,
stackRegex: /errors\.cy\.js:26/,
})
})
})
const angularVersions = [13, 14] as const
angularVersions.forEach((angularVersion) => {
describe(`Angular ${angularVersion}`, {
viewportHeight: 768,
viewportWidth: 1024,
// Limiting tests kept in memory due to large memory cost
// of nested spec snapshots
numTestsKeptInMemory: 1,
}, () => {
beforeEach(() => {
cy.scaffoldProject(`angular-${angularVersion}`)
})
it('error conditions', () => {
const verify = loadErrorSpec({
projectName: `angular-${angularVersion}`,
configFile: 'cypress.config.ts',
filePath: 'src/app/errors.cy.ts',
failCount: 1,
})
// Angular uses ZoneJS which encapsulates errors thrown by components
// Thus, the mount, sync, and async error case errors will not propagate to Cypress
// and thus won't fail the tests
verify('command failure', {
line: 21,
column: 8,
command: 'get',
message: [
'Timed out retrying',
'element-that-does-not-exist',
],
})
})
})
})

View File

@@ -0,0 +1,704 @@
import * as specLoader from './support/spec-loader'
import { createVerify, verifyInternalFailure } from './support/verify-failures'
type VerifyFunc = (specTitle: string, verifyOptions: any) => void
/**
* Navigates to desired error spec file within Cypress app and waits for completion.
* Returns scoped verify function to aid inner spec validation.
*/
function loadErrorSpec (options: specLoader.LoadSpecOptions, configFile: string): VerifyFunc {
const DefaultOptions: Partial<specLoader.LoadSpecOptions> = {
projectName: 'runner-ct-specs',
mode: 'component',
configFile,
}
const effectiveOptions = {
...DefaultOptions,
...options,
}
const {
filePath,
hasPreferredIde = false,
mode,
} = effectiveOptions
specLoader.loadSpec(effectiveOptions)
// Return scoped verify function with spec options baked in
return createVerify({ fileName: Cypress._.last(filePath.split('/')), hasPreferredIde, mode })
}
/**
* Generate CT error reporting specs for a given dev server - have to structure this way to avoid
* Out of Memory issues if they're all contained in a single spec
*
* @param server 'Vite' | 'Webpack'
* @param configFile config file
*/
export const generateCtErrorTests = (server: 'Webpack' | 'Vite', configFile: string) => {
describe(`${server} - errors ui`, {
viewportHeight: 768,
viewportWidth: 1024,
// Limiting tests kept in memory due to large memory cost
// of nested spec snapshots
numTestsKeptInMemory: 1,
}, () => {
it('assertion failures', () => {
const verify = loadErrorSpec({
filePath: 'errors/assertions.cy.js',
hasPreferredIde: true,
failCount: 3,
}, configFile)
verify('with expect().<foo>', {
line: 3,
column: [25, 26],
message: `expected 'actual' to equal 'expected'`,
})
verify('with assert()', {
line: 7,
column: [5, 6, 12], // [chrome, firefox]
message: `should be true`,
})
verify('with assert.<foo>()', {
line: 11,
column: [12, 13],
message: `expected 'actual' to equal 'expected'`,
})
})
it('assertion failures - no preferred IDE', () => {
const verify = loadErrorSpec({
filePath: 'errors/assertions.cy.js',
failCount: 3,
}, configFile)
verify('with expect().<foo>', {
column: [25, 26],
message: `expected 'actual' to equal 'expected'`,
codeFrameText: 'with expect().<foo>',
})
})
// Vite does not provide the `require` syntax used by this test
if (server === 'Webpack') {
it('exception failures', () => {
const verify = loadErrorSpec({
filePath: 'errors/exceptions.cy.js',
failCount: 2,
}, configFile)
verify('in spec file', {
column: 10,
message: 'bar is not a function',
})
verify('in file outside project', {
message: 'An outside error',
stackRegex: /\/throws\-error\.js:5:8/,
codeFrameRegex: /\/throws\-error\.js:5:9/,
codeFrameText: `thrownewError('An outside error')`,
})
})
}
it('hooks', { viewportHeight: 900 }, () => {
const verify = loadErrorSpec({
filePath: 'errors/hooks.cy.js',
failCount: 1,
}, configFile)
// https://github.com/cypress-io/cypress/issues/8214
// https://github.com/cypress-io/cypress/issues/8288
// https://github.com/cypress-io/cypress/issues/8350
verify('test', {
column: [7, 8, 18], // [chrome, firefox]
codeFrameText: 'beforeEach(()=>',
message: `Cypress detected you registered a(n) beforeEach hook while a test was running`,
})
})
it('commands', () => {
const verify = loadErrorSpec({
filePath: 'errors/commands.cy.js',
failCount: 2,
}, configFile)
verify('failure', {
column: [8, 9],
message: 'Timed out retrying after 0ms: Expected to find element: #does-not-exist, but never found it',
})
verify('chained failure', {
column: [20, 21],
message: 'Timed out retrying after 0ms: Expected to find element: #does-not-exist, but never found it',
})
})
it('cy.then', () => {
const verify = loadErrorSpec({
filePath: 'errors/then.cy.js',
failCount: 3,
}, configFile)
verify('assertion failure', {
column: [27, 28],
message: `expected 'actual' to equal 'expected'`,
})
verify('exception', {
column: [12, 13],
message: 'bar is not a function',
})
verify('command failure', {
column: [10, 11],
message: 'Timed out retrying after 0ms: Expected to find element: #does-not-exist, but never found it',
})
})
it('cy.should', () => {
const verify = loadErrorSpec({
filePath: 'errors/should.cy.js',
failCount: 8,
}, configFile)
verify('callback assertion failure', {
column: [27, 28],
message: `expected 'actual' to equal 'expected'`,
})
verify('callback exception', {
column: [12, 13],
message: 'bar is not a function',
})
verify('standard assertion failure', {
column: [6, 7],
message: 'Timed out retrying after 0ms: expected {} to have property \'foo\'',
})
verify('after multiple', {
column: [6, 7],
message: 'Timed out retrying after 0ms: expected \'foo\' to equal \'bar\'',
})
verify('after multiple callbacks exception', {
column: [12, 13],
codeFrameText: '({}).bar()',
message: 'bar is not a function',
})
verify('after multiple callbacks assertion failure', {
column: [27, 28],
codeFrameText: '.should(()=>',
message: `expected 'actual' to equal 'expected'`,
})
verify('after callback success assertion failure', {
column: [6, 7],
codeFrameText: '.should(\'have.property',
message: `expected {} to have property 'foo'`,
})
verify('command failure after success', {
column: [8, 9],
message: 'Timed out retrying after 0ms: Expected to find element: #does-not-exist, but never found it',
})
})
it('cy.each', () => {
const verify = loadErrorSpec({
filePath: 'errors/each.cy.js',
failCount: 3,
}, configFile)
verify('assertion failure', {
column: [27, 28],
message: `expected 'actual' to equal 'expected'`,
})
verify('exception', {
column: [12, 13],
message: 'bar is not a function',
})
verify('command failure', {
column: [10, 11],
message: 'Expected to find element: #does-not-exist, but never found it',
})
})
it('cy.spread', () => {
const verify = loadErrorSpec({
filePath: 'errors/spread.cy.js',
failCount: 3,
}, configFile)
verify('assertion failure', {
column: [27, 28],
message: `expected 'actual' to equal 'expected'`,
})
verify('exception', {
column: [12, 13],
message: 'bar is not a function',
})
verify('command failure', {
column: [10, 11],
message: 'Expected to find element: #does-not-exist, but never found it',
})
})
it('cy.within', () => {
const verify = loadErrorSpec({
filePath: 'errors/within.cy.js',
failCount: 3,
}, configFile)
verify('assertion failure', {
column: [27, 28],
message: `expected 'actual' to equal 'expected'`,
})
verify('exception', {
column: [12, 13],
message: 'bar is not a function',
})
verify('command failure', {
column: [10, 11],
message: 'Expected to find element: #does-not-exist, but never found it',
})
})
it('cy.wrap', () => {
const verify = loadErrorSpec({
filePath: 'errors/wrap.cy.js',
failCount: 3,
}, configFile)
verify('assertion failure', {
column: [27, 28],
message: `expected 'actual' to equal 'expected'`,
})
verify('exception', {
column: [12, 13],
message: 'bar is not a function',
})
verify('command failure', {
column: [10, 11],
message: 'Expected to find element: #does-not-exist, but never found it',
})
})
it('cy.intercept', () => {
const verify = loadErrorSpec({
filePath: 'errors/intercept.cy.ts',
failCount: 3,
}, configFile)
verify('assertion failure in request callback', {
column: 22,
message: [
`expected 'a' to equal 'b'`,
],
notInMessage: [
'The following error originated from your spec code',
],
})
verify('assertion failure in response callback', {
column: 24,
codeFrameText: '.reply(()=>{',
message: [
`expected 'b' to equal 'c'`,
],
notInMessage: [
'The following error originated from your spec code',
],
})
verify('fails when erroneous response is received while awaiting response', {
column: 6,
// TODO: determine why code frame output is different in run/open mode
// this fails the active test because it's an asynchronous
// response failure from the network
// codeFrameText: '.wait(1000)',
hasCodeFrame: false,
message: [
'A callback was provided to intercept the upstream response, but a network error occurred while making the request',
],
notInMessage: [
'The following error originated from your spec code',
],
})
})
it('cy.readFile', () => {
const verify = loadErrorSpec({
filePath: 'errors/readfile.cy.js',
failCount: 1,
}, configFile)
verify('existence failure', {
column: [8, 9],
message: 'failed because the file does not exist',
})
})
it('validation errors', () => {
const verify = loadErrorSpec({
filePath: 'errors/validation.cy.js',
failCount: 3,
}, configFile)
verify('from cypress', {
column: [8, 9],
message: 'can only accept a string preset or',
stack: ['throwErrBadArgs', 'From Your Spec Code:'],
})
verify('from chai expect', {
column: [5, 6, 12], // [chrome, firefox]
message: 'Invalid Chai property: nope',
stack: ['proxyGetter', 'From Your Spec Code:'],
})
verify('from chai assert', {
column: [12, 13],
message: 'object tested must be an array',
})
})
it('event handlers', () => {
const verify = loadErrorSpec({
filePath: 'errors/events.cy.js',
failCount: 4,
}, configFile)
verify('event assertion failure', {
column: [27, 28],
message: `expected 'actual' to equal 'expected'`,
})
verify('event exception', {
column: [12, 13],
message: 'bar is not a function',
})
verify('fail handler assertion failure', {
column: [27, 28],
message: `expected 'actual' to equal 'expected'`,
})
verify('fail handler exception', {
column: [12, 13],
message: 'bar is not a function',
})
})
it('custom commands', () => {
const verify = loadErrorSpec({
filePath: 'errors/custom_commands.cy.js',
failCount: 3,
}, configFile)
verify('assertion failure', {
column: [23, 24],
message: `expected 'actual' to equal 'expected'`,
codeFrameText: `add('failAssertion'`,
})
verify('exception', {
column: [8, 9],
message: 'bar is not a function',
codeFrameText: `add('failException'`,
})
verify('command failure', {
column: [6, 7],
message: 'Timed out retrying after 0ms: Expected to find element: #does-not-exist, but never found it',
codeFrameText: `add('failCommand'`,
})
})
it('typescript', () => {
const verify = loadErrorSpec({
filePath: 'errors/typescript.cy.ts',
failCount: 3,
}, configFile)
verify('assertion failure', {
column: [25, 26],
message: `expected 'actual' to equal 'expected'`,
})
verify('exception', {
column: [10, 11],
message: 'bar is not a function',
})
verify('command failure', {
column: [8, 9],
message: 'Timed out retrying after 0ms: Expected to find element: #does-not-exist, but never found it',
codeFrameText: `.get('#does-not-exist')`,
})
})
context('docs url', () => {
before(() => {
// docs_url.cy.js manually sets the 'isInteractive' config property in order
// to test both run and open mode behavior. We need to skip the config validation
// here in order for this to be possible.
// @ts-ignore
window.top.__cySkipValidateConfig = true
})
after(() => {
// @ts-ignore
window.top.__cySkipValidateConfig = false
})
it('docs url validation', { retries: 1 }, () => {
const docsUrl = 'https://on.cypress.io/viewport'
const verify = loadErrorSpec({
filePath: 'errors/docs_url.cy.js',
failCount: 2,
}, configFile)
verify('displays as link in interactive mode', {
verifyFn () {
cy.contains('.runnable-title', 'displays as link in interactive mode')
.closest('.runnable').within(() => {
cy
.get('.runnable-err-message')
.should('not.contain', docsUrl)
.contains('Learn more')
.should('have.attr', 'href', docsUrl)
})
},
})
verify('is text in error message in run mode', {
verifyFn () {
cy.contains('.runnable-title', 'is text in error message in run mode')
.closest('.runnable').within(() => {
cy
.get('.runnable-err-message')
.should('contain', docsUrl)
.contains('Learn more')
.should('not.exist')
})
},
})
})
})
// cases where there is a bug in Cypress and we should show cypress internals
// instead of the invocation stack. we test this by monkey-patching internal
// methods to make them throw an error
it('unexpected errors', () => {
const verify = loadErrorSpec({
filePath: 'errors/unexpected.cy.js',
failCount: 2,
}, configFile)
verify('Cypress method error', {
verifyFn: verifyInternalFailure,
method: 'Cypress.LocalStorage.clear',
})
verify('internal cy error', {
verifyFn: verifyInternalFailure,
method: 'cy.expect',
})
})
})
}
export const generateCtUncaughtErrorTests = (server: 'Webpack' | 'Vite', configFile: string) => {
it('uncaught errors', () => {
const verify = loadErrorSpec({
filePath: 'errors/uncaught-ct.cy.js',
failCount: 11,
}, configFile)
verify('sync app mount exception', {
uncaught: true,
originalMessage: 'mount error',
message: [
'The following error originated from your test code',
],
notInMessage: [
'It was caused by an unhandled promise rejection',
],
regex: /src\/Errors.jsx/,
hasCodeFrame: false,
})
verify('sync app exception after mount', {
uncaught: true,
originalMessage: 'sync error',
message: [
'The following error originated from your test code',
],
notInMessage: [
'It was caused by an unhandled promise rejection',
],
regex: /src\/Errors.jsx/,
hasCodeFrame: false,
})
verify('exception inside uncaught:exception', {
uncaught: true,
uncaughtMessage: 'mount error',
column: [5, 6, 12],
originalMessage: 'bar is not a function',
message: [
'The following error originated from your test code',
],
notInMessage: [
'It was caused by an unhandled promise rejection',
],
})
verify('async app exception after mount', {
uncaught: true,
originalMessage: 'async error',
message: [
'The following error originated from your test code',
],
notInMessage: [
'It was caused by an unhandled promise rejection',
],
regex: /src\/Errors.jsx/,
hasCodeFrame: false,
})
verify('app unhandled rejection', {
uncaught: true,
originalMessage: 'promise rejection',
message: [
'The following error originated from your test code',
'It was caused by an unhandled promise rejection',
],
regex: /src\/Errors.jsx/,
hasCodeFrame: false,
})
verify('async spec exception', {
uncaught: true,
column: [3, 5, 12],
originalMessage: 'bar is not a function',
message: [
'The following error originated from your test code',
],
notInMessage: [
'It was caused by an unhandled promise rejection',
],
})
verify('async spec exception with done', {
uncaught: true,
column: [3, 6, 12],
originalMessage: 'bar is not a function',
message: [
'The following error originated from your test code',
],
notInMessage: [
'It was caused by an unhandled promise rejection',
],
})
verify('spec unhandled rejection', {
uncaught: true,
column: [20, 21],
originalMessage: 'Unhandled promise rejection from the spec',
message: [
'The following error originated from your test code',
'It was caused by an unhandled promise rejection',
],
})
verify('spec unhandled rejection with done', {
uncaught: true,
column: [20, 21],
originalMessage: 'Unhandled promise rejection from the spec',
message: [
'The following error originated from your application code',
'It was caused by an unhandled promise rejection',
],
hasCodeFrame: server !== 'Vite',
})
verify('spec Bluebird unhandled rejection', {
uncaught: true,
column: [21, 22],
originalMessage: 'Unhandled promise rejection from the spec',
message: [
'The following error originated from your test code',
'It was caused by an unhandled promise rejection',
],
hasCodeFrame: server !== 'Vite',
})
verify('spec Bluebird unhandled rejection with done', {
uncaught: true,
column: [21, 22],
originalMessage: 'Unhandled promise rejection from the spec',
message: [
'The following error originated from your test code',
'It was caused by an unhandled promise rejection',
],
hasCodeFrame: server !== 'Vite',
})
})
it('uncaught errors: outside test', () => {
const verify = loadErrorSpec({
filePath: 'errors/uncaught_outside_test.cy.js',
failCount: 1,
}, configFile)
// NOTE: the following 2 test don't have uncaught: true because we don't
// display command logs if there are only events and not true commands
// and uncaught: true causes the verification to look for the error
// event command log
verify('An uncaught error was detected outside of a test', {
column: [7, 8],
message: [
'The following error originated from your test code',
'error from outside test',
'Cypress could not associate this error to any specific test',
],
codeFrameText: `thrownewError('error from outside test')`,
})
})
it('uncaught errors: outside test only suite', () => {
const verify = loadErrorSpec({
filePath: 'errors/uncaught_outside_test_only_suite.cy.js',
failCount: 1,
}, configFile)
verify('An uncaught error was detected outside of a test', {
column: [7, 8],
message: [
'error from outside test with only suite',
'The following error originated from your test code',
'Cypress could not associate this error to any specific test',
],
codeFrameText: `thrownewError('error from outside test with only suite')`,
})
})
}

View File

@@ -0,0 +1,3 @@
import { generateCtErrorTests } from './reporter-ct-generator'
generateCtErrorTests('Vite', 'cypress-vite.config.js')

View File

@@ -0,0 +1,3 @@
import { generateCtUncaughtErrorTests } from './reporter-ct-generator'
generateCtUncaughtErrorTests('Vite', 'cypress-vite.config.js')

View File

@@ -0,0 +1,3 @@
import { generateCtErrorTests } from './reporter-ct-generator'
generateCtErrorTests('Webpack', 'cypress-webpack.config.js')

View File

@@ -0,0 +1,3 @@
import { generateCtUncaughtErrorTests } from './reporter-ct-generator'
generateCtUncaughtErrorTests('Webpack', 'cypress-webpack.config.js')

View File

@@ -11,12 +11,13 @@ function loadErrorSpec (options: specLoader.LoadSpecOptions): VerifyFunc {
const {
filePath,
hasPreferredIde = false,
mode,
} = options
specLoader.loadSpec(options)
// Return scoped verify function with spec options baked in
return createVerify({ fileName: Cypress._.last(filePath.split('/')), hasPreferredIde })
return createVerify({ fileName: Cypress._.last(filePath.split('/')), hasPreferredIde, mode })
}
describe('errors ui', {

View File

@@ -3,11 +3,11 @@ export const shouldHaveTestResults = ({ passCount, failCount, pendingCount }) =>
failCount = failCount || '--'
cy.findByLabelText('Stats', { timeout: 10000 }).within(() => {
cy.get('.passed .num', { timeout: 10000 }).should('have.text', `${passCount}`)
cy.get('.failed .num', { timeout: 10000 }).should('have.text', `${failCount}`)
cy.get('.passed .num', { timeout: 30000 }).should('have.text', `${passCount}`)
cy.get('.failed .num', { timeout: 30000 }).should('have.text', `${failCount}`)
if (pendingCount) {
cy.get('.pending .num', { timeout: 10000 }).should('have.text', `${pendingCount}`)
cy.get('.pending .num', { timeout: 20000 }).should('have.text', `${pendingCount}`)
}
})
}
@@ -19,7 +19,10 @@ export type LoadSpecOptions = {
failCount?: number | string
pendingCount?: number | string
hasPreferredIde?: boolean
projectName?: 'runner-e2e-specs' | 'session-and-origin-e2e-specs'
projectName?: 'runner-e2e-specs' | 'runner-ct-specs' | 'session-and-origin-e2e-specs'
mode?: 'e2e' | 'component'
configFile?: string
scaffold?: boolean
}
export function loadSpec (options: LoadSpecOptions) {
@@ -30,12 +33,18 @@ export function loadSpec (options: LoadSpecOptions) {
failCount = '--',
hasPreferredIde = false,
pendingCount,
mode = 'e2e',
configFile = 'cypress.config.js',
projectName = 'runner-e2e-specs',
scaffold = true,
} = options
cy.scaffoldProject(projectName)
cy.openProject(projectName)
cy.startAppServer()
if (scaffold) {
cy.scaffoldProject(projectName)
}
cy.openProject(projectName, ['--config-file', configFile])
cy.startAppServer(mode)
cy.withCtx((ctx, options) => {
ctx.update((coreData) => {

View File

@@ -42,6 +42,7 @@ const verifyFailure = (options) => {
uncaughtMessage,
line,
regex,
mode,
} = options
let { codeFrameText, stackRegex, codeFrameRegex } = options
@@ -130,7 +131,7 @@ const verifyFailure = (options) => {
hasPreferredIde,
action: () => {
cy.get('@Root').contains('.runnable-err-stack-trace .runnable-err-file-path a', fileName)
.click('left')
.click({ force: true })
},
line,
column: stackColumn,
@@ -151,7 +152,8 @@ const verifyFailure = (options) => {
if (uncaught) {
cy.log('uncaught error has an associated log for the original error')
cy.get('.command-name-uncaught-exception')
.should('have.length', 1)
// https://github.com/cypress-io/cypress/issues/23920
.should(mode === 'component' ? 'have.length.gte' : 'have.length', 1)
.find('.command-state-failed')
.find('.command-message-text')
.should('include.text', uncaughtMessage || originalMessage)
@@ -168,7 +170,7 @@ const verifyFailure = (options) => {
.invoke('text')
.should('match', codeFrameRegex)
cy.get('.test-err-code-frame pre span').should('include.text', codeFrameText)
cy.get('.test-err-code-frame span').should('include.text', codeFrameText)
})
if (verifyOpenInIde) {
@@ -185,11 +187,12 @@ const verifyFailure = (options) => {
}
}
export const createVerify = ({ fileName, hasPreferredIde }) => {
export const createVerify = ({ fileName, hasPreferredIde, mode }) => {
return (specTitle: string, props: any) => {
props.specTitle ||= specTitle
props.fileName ||= fileName
props.hasPreferredIde = hasPreferredIde
props.mode = mode
;(props.verifyFn || verifyFailure).call(null, props)
}
@@ -206,10 +209,5 @@ export const verifyInternalFailure = (props) => {
cy.get('.runnable-err-stack-trace')
.should('include.text', stackMethod || method)
// this is an internal cypress error and we can only show code frames
// from specs, so it should not show the code frame
cy.get('.test-err-code-frame')
.should('not.exist')
})
}

View File

@@ -329,8 +329,6 @@ class $Cypress {
this.events.proxyTo(this.cy)
$scriptUtils.runScripts(specWindow, scripts)
// TODO: remove this after making the type of `runScripts` more specific.
// @ts-expect-error
.catch((error) => {
this.runner.onSpecError('error')({ error })
})

View File

@@ -1,4 +1,3 @@
import _ from 'lodash'
import Bluebird from 'bluebird'
import $networkUtils from './network_utils'
@@ -25,11 +24,15 @@ const extractSourceMap = ([script, contents]) => {
}
const evalScripts = (specWindow, scripts: any = []) => {
_.each(scripts, ([script, contents]) => {
specWindow.eval(`${contents}\n//# sourceURL=${script.fullyQualifiedUrl}`)
})
return Bluebird.each(scripts, (_script: any) => {
const [script, contents] = _script
return null
if (script.load) {
return script.load()
}
return specWindow.eval(`${contents}\n//# sourceURL=${script.fullyQualifiedUrl}`)
})
}
const runScriptsFromUrls = (specWindow, scripts) => {

View File

@@ -6,6 +6,7 @@ import type { BasicSourceMapConsumer } from 'source-map'
import mappingsWasm from 'source-map/lib/mappings.wasm'
import $utils from './utils'
import stackUtils from './stack_utils'
const sourceMapExtractionRegex = /\/\/\s*[@#]\s*sourceMappingURL\s*=\s*(data:[^\s]*)/g
const regexDataUrl = /data:[^;\n]+(?:;charset=[^;\n]+)?;base64,([a-zA-Z0-9+/]+={0,2})/ // matches data urls
@@ -22,7 +23,7 @@ const initializeSourceMapConsumer = async (script, sourceMap): Promise<BasicSour
const consumer = await new SourceMapConsumer(sourceMap)
sourceMapConsumers[script.fullyQualifiedUrl] = consumer
sourceMapConsumers[stackUtils.toPosix(script.fullyQualifiedUrl)] = consumer
return consumer
}
@@ -55,10 +56,12 @@ const extractSourceMap = (fileContents) => {
}
const getSourceContents = (filePath, sourceFile) => {
if (!sourceMapConsumers[filePath]) return null
const posixFilePath = stackUtils.toPosix(filePath)
if (!sourceMapConsumers[posixFilePath]) return null
try {
return sourceMapConsumers[filePath].sourceContentFor(sourceFile)
return sourceMapConsumers[posixFilePath].sourceContentFor(sourceFile)
} catch (err) {
// ignore the sourceFile not being in the source map. there's nothing we
// can do about it and we don't want to thrown an exception
@@ -69,7 +72,8 @@ const getSourceContents = (filePath, sourceFile) => {
}
const getSourcePosition = (filePath, position) => {
const sourceMapConsumer = sourceMapConsumers[filePath]
const posixFilePath = stackUtils.toPosix(filePath)
const sourceMapConsumer = sourceMapConsumers[posixFilePath]
if (!sourceMapConsumer) return null

View File

@@ -12,6 +12,8 @@ import { getStackLines, replacedStack, stackWithoutMessage, splitStack, unsplitS
const whitespaceRegex = /^(\s*)*/
const customProtocolRegex = /^[^:\/]+:\/{1,3}/
// Find 'namespace' values (like `_N_E` for Next apps) without adjusting relative paths (like `../`)
const webpackDevtoolNamespaceRegex = /webpack:\/{2}([^.]*)?\.\//
const percentNotEncodedRegex = /%(?![0-9A-F][0-9A-F])/g
const webkitStackLineRegex = /(.*)@(.*)(\n?)/g
@@ -149,13 +151,17 @@ const getCodeFrameFromSource = (sourceCode, { line, column: originalColumn, rela
}
}
const getRelativePathFromRoot = (relativeFile, absoluteFile) => {
export const toPosix = (file: string) => {
return Cypress.config('platform') === 'win32'
? file.replaceAll('\\', '/')
: file
}
const getRelativePathFromRoot = (relativeFile: string, absoluteFile?: string) => {
// at this point relativeFile is relative to the cypress config
// we need it to be relative to the repo root, which is different for monorepos
const repoRoot = Cypress.config('repoRoot')
const posixAbsoluteFile = (Cypress.config('platform') === 'win32')
? absoluteFile?.replaceAll('\\', '/')
: absoluteFile
const posixAbsoluteFile = absoluteFile ? toPosix(absoluteFile) : ''
if (posixAbsoluteFile?.startsWith(`${repoRoot}/`)) {
return posixAbsoluteFile.replace(`${repoRoot}/`, '')
@@ -289,6 +295,13 @@ const stripCustomProtocol = (filePath) => {
return
}
// Check the path to see if custom namespaces have been applied and, if so, remove them
// For example, in Next.js we end up with paths like `_N_E/pages/index.cy.js`, and we
// need to strip off the `_N_E` so that "Open in IDE" links work correctly
if (webpackDevtoolNamespaceRegex.test(filePath)) {
return filePath.replace(webpackDevtoolNamespaceRegex, '')
}
return filePath.replace(customProtocolRegex, '')
}
@@ -328,6 +341,10 @@ const getSourceDetailsForLine = (projectRoot, line): LineDetail => {
if (relativeFile) {
relativeFile = path.normalize(relativeFile)
if (relativeFile.includes(projectRoot)) {
relativeFile = relativeFile.replace(projectRoot, '').substring(1)
}
}
let absoluteFile
@@ -471,7 +488,7 @@ const normalizedUserInvocationStack = (userInvocationStack) => {
// at cypressErr (cypress:///../driver/src/cypress/error_utils.js:259:17)
// stacks in prod builds look like:
// at cypressErr (http://localhost:3500/isolated-runner/cypress_runner.js:173123:17)
return line.includes('cy[name]') || line.includes('Chainer.prototype[key]')
return line.includes('cy[name]') || line.includes('Chainer.prototype[key]') || line.includes('cy.<computed>') || line.includes('$Chainer.<computed>')
}).join('\n')
return normalizeStackIndentation(winnowedStackLines)
@@ -495,4 +512,5 @@ export default {
stackWithUserInvocationStackSpliced,
captureUserInvocationStack,
getInvocationDetails,
toPosix,
}

View File

@@ -7,15 +7,15 @@ exports['@cypress/vite-dev-server react executes all of the tests for vite3.0.2-
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 1.2.3 │
│ Browser: FooBrowser 88 │
│ Specs: 6 found (App.cy.jsx, AppCompilationError.cy.jsx, MissingReact.cy.jsx, MissingReact │
│ InSpec.cy.jsx, Rerendering.cy.jsx, Unmount.cy.jsx)
│ Specs: 7 found (App.cy.jsx, AppCompilationError.cy.jsx, Errors.cy.jsx, MissingReact.cy.js
x, MissingReactInSpec.cy.jsx, Rerendering.cy.jsx, Unmount.cy.jsx) │
│ Searched: **/*.cy.{js,jsx,ts,tsx} │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: App.cy.jsx (1 of 6)
Running: App.cy.jsx (1 of 7)
✓ renders hello world
@@ -46,7 +46,7 @@ exports['@cypress/vite-dev-server react executes all of the tests for vite3.0.2-
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: AppCompilationError.cy.jsx (2 of 6)
Running: AppCompilationError.cy.jsx (2 of 7)
1) An uncaught error was detected outside of a test
@@ -98,7 +98,89 @@ We dynamically generated a new test to display this failure.
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReact.cy.jsx (3 of 6)
Running: Errors.cy.jsx (3 of 7)
Errors
1) error on mount
2) sync error
3) async error
4) command failure
0 passing
4 failing
1) Errors
error on mount:
Error: The following error originated from your test code, not from Cypress.
> mount error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
2) Errors
sync error:
Error: The following error originated from your test code, not from Cypress.
> sync error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
3) Errors
async error:
Error: The following error originated from your test code, not from Cypress.
> async error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
4) Errors
command failure:
AssertionError: Timed out retrying after 50ms: Expected to find element: \`element-that-does-not-exist\`, but never found it.
[stack trace lines]
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 4 │
│ Passing: 0 │
│ Failing: 4 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 4 │
│ Video: true │
│ Duration: X seconds │
│ Spec Ran: Errors.cy.jsx │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(Screenshots)
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- error on mount (failed) (1280x720)
.png
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- sync error (failed).png (1280x720)
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- async error (failed).pn (1280x720)
g
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- command failure (failed (1280x720)
).png
(Video)
- Started processing: Compressing to 32 CRF
- Finished processing: /XXX/XXX/XXX/cypress/videos/Errors.cy.jsx.mp4 (X second)
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReact.cy.jsx (4 of 7)
1) is missing React
@@ -146,7 +228,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReactInSpec.cy.jsx (4 of 6)
Running: MissingReactInSpec.cy.jsx (5 of 7)
1) is missing React in this file
@@ -190,7 +272,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: Rerendering.cy.jsx (5 of 6)
Running: Rerendering.cy.jsx (6 of 7)
re-render
@@ -223,7 +305,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: Unmount.cy.jsx (6 of 6)
Running: Unmount.cy.jsx (7 of 7)
Comp with componentWillUnmount
@@ -269,6 +351,8 @@ When Cypress detects uncaught errors originating from your test code it will aut
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ AppCompilationError.cy.jsx XX:XX 1 - 1 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ Errors.cy.jsx XX:XX 4 - 4 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ MissingReact.cy.jsx XX:XX 1 - 1 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ MissingReactInSpec.cy.jsx XX:XX 1 - 1 - - │
@@ -277,7 +361,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✔ Unmount.cy.jsx XX:XX 3 3 - - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
3 of 6 failed (50%) XX:XX 8 5 3 - -
4 of 7 failed (57%) XX:XX 12 5 7 - -
`
@@ -291,15 +375,15 @@ exports['@cypress/vite-dev-server react executes all of the tests for vite2.8.6-
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 1.2.3 │
│ Browser: FooBrowser 88 │
│ Specs: 6 found (App.cy.jsx, AppCompilationError.cy.jsx, MissingReact.cy.jsx, MissingReact │
│ InSpec.cy.jsx, Rerendering.cy.jsx, Unmount.cy.jsx)
│ Specs: 7 found (App.cy.jsx, AppCompilationError.cy.jsx, Errors.cy.jsx, MissingReact.cy.js
x, MissingReactInSpec.cy.jsx, Rerendering.cy.jsx, Unmount.cy.jsx) │
│ Searched: **/*.cy.{js,jsx,ts,tsx} │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: App.cy.jsx (1 of 6)
Running: App.cy.jsx (1 of 7)
✓ renders hello world
@@ -330,7 +414,7 @@ exports['@cypress/vite-dev-server react executes all of the tests for vite2.8.6-
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: AppCompilationError.cy.jsx (2 of 6)
Running: AppCompilationError.cy.jsx (2 of 7)
1) An uncaught error was detected outside of a test
@@ -382,7 +466,89 @@ We dynamically generated a new test to display this failure.
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReact.cy.jsx (3 of 6)
Running: Errors.cy.jsx (3 of 7)
Errors
1) error on mount
2) sync error
3) async error
4) command failure
0 passing
4 failing
1) Errors
error on mount:
Error: The following error originated from your test code, not from Cypress.
> mount error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
2) Errors
sync error:
Error: The following error originated from your test code, not from Cypress.
> sync error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
3) Errors
async error:
Error: The following error originated from your test code, not from Cypress.
> async error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
4) Errors
command failure:
AssertionError: Timed out retrying after 50ms: Expected to find element: \`element-that-does-not-exist\`, but never found it.
[stack trace lines]
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 4 │
│ Passing: 0 │
│ Failing: 4 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 4 │
│ Video: true │
│ Duration: X seconds │
│ Spec Ran: Errors.cy.jsx │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(Screenshots)
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- error on mount (failed) (1280x720)
.png
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- sync error (failed).png (1280x720)
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- async error (failed).pn (1280x720)
g
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- command failure (failed (1280x720)
).png
(Video)
- Started processing: Compressing to 32 CRF
- Finished processing: /XXX/XXX/XXX/cypress/videos/Errors.cy.jsx.mp4 (X second)
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReact.cy.jsx (4 of 7)
1) is missing React
@@ -430,7 +596,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReactInSpec.cy.jsx (4 of 6)
Running: MissingReactInSpec.cy.jsx (5 of 7)
1) is missing React in this file
@@ -474,7 +640,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: Rerendering.cy.jsx (5 of 6)
Running: Rerendering.cy.jsx (6 of 7)
re-render
@@ -507,7 +673,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: Unmount.cy.jsx (6 of 6)
Running: Unmount.cy.jsx (7 of 7)
Comp with componentWillUnmount
@@ -553,6 +719,8 @@ When Cypress detects uncaught errors originating from your test code it will aut
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ AppCompilationError.cy.jsx XX:XX 1 - 1 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ Errors.cy.jsx XX:XX 4 - 4 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ MissingReact.cy.jsx XX:XX 1 - 1 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ MissingReactInSpec.cy.jsx XX:XX 1 - 1 - - │
@@ -561,7 +729,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✔ Unmount.cy.jsx XX:XX 3 3 - - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
3 of 6 failed (50%) XX:XX 8 5 3 - -
4 of 7 failed (57%) XX:XX 12 5 7 - -
`
@@ -575,15 +743,15 @@ exports['@cypress/vite-dev-server react executes all of the tests for vite2.9.1-
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 1.2.3 │
│ Browser: FooBrowser 88 │
│ Specs: 6 found (App.cy.jsx, AppCompilationError.cy.jsx, MissingReact.cy.jsx, MissingReact │
│ InSpec.cy.jsx, Rerendering.cy.jsx, Unmount.cy.jsx)
│ Specs: 7 found (App.cy.jsx, AppCompilationError.cy.jsx, Errors.cy.jsx, MissingReact.cy.js
x, MissingReactInSpec.cy.jsx, Rerendering.cy.jsx, Unmount.cy.jsx) │
│ Searched: **/*.cy.{js,jsx,ts,tsx} │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: App.cy.jsx (1 of 6)
Running: App.cy.jsx (1 of 7)
✓ renders hello world
@@ -614,7 +782,7 @@ exports['@cypress/vite-dev-server react executes all of the tests for vite2.9.1-
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: AppCompilationError.cy.jsx (2 of 6)
Running: AppCompilationError.cy.jsx (2 of 7)
1) An uncaught error was detected outside of a test
@@ -666,7 +834,89 @@ We dynamically generated a new test to display this failure.
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReact.cy.jsx (3 of 6)
Running: Errors.cy.jsx (3 of 7)
Errors
1) error on mount
2) sync error
3) async error
4) command failure
0 passing
4 failing
1) Errors
error on mount:
Error: The following error originated from your test code, not from Cypress.
> mount error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
2) Errors
sync error:
Error: The following error originated from your test code, not from Cypress.
> sync error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
3) Errors
async error:
Error: The following error originated from your test code, not from Cypress.
> async error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
4) Errors
command failure:
AssertionError: Timed out retrying after 50ms: Expected to find element: \`element-that-does-not-exist\`, but never found it.
[stack trace lines]
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 4 │
│ Passing: 0 │
│ Failing: 4 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 4 │
│ Video: true │
│ Duration: X seconds │
│ Spec Ran: Errors.cy.jsx │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(Screenshots)
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- error on mount (failed) (1280x720)
.png
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- sync error (failed).png (1280x720)
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- async error (failed).pn (1280x720)
g
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- command failure (failed (1280x720)
).png
(Video)
- Started processing: Compressing to 32 CRF
- Finished processing: /XXX/XXX/XXX/cypress/videos/Errors.cy.jsx.mp4 (X second)
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReact.cy.jsx (4 of 7)
1) is missing React
@@ -714,7 +964,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReactInSpec.cy.jsx (4 of 6)
Running: MissingReactInSpec.cy.jsx (5 of 7)
1) is missing React in this file
@@ -758,7 +1008,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: Rerendering.cy.jsx (5 of 6)
Running: Rerendering.cy.jsx (6 of 7)
re-render
@@ -791,7 +1041,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: Unmount.cy.jsx (6 of 6)
Running: Unmount.cy.jsx (7 of 7)
Comp with componentWillUnmount
@@ -837,6 +1087,8 @@ When Cypress detects uncaught errors originating from your test code it will aut
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ AppCompilationError.cy.jsx XX:XX 1 - 1 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ Errors.cy.jsx XX:XX 4 - 4 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ MissingReact.cy.jsx XX:XX 1 - 1 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ MissingReactInSpec.cy.jsx XX:XX 1 - 1 - - │
@@ -845,7 +1097,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✔ Unmount.cy.jsx XX:XX 3 3 - - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
3 of 6 failed (50%) XX:XX 8 5 3 - -
4 of 7 failed (57%) XX:XX 12 5 7 - -
`

View File

@@ -10,15 +10,15 @@ exports['@cypress/webpack-dev-server react executes all of the tests for webpack
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 1.2.3 │
│ Browser: FooBrowser 88 │
│ Specs: 6 found (App.cy.jsx, AppCompilationError.cy.jsx, MissingReact.cy.jsx, MissingReact │
│ InSpec.cy.jsx, Rerendering.cy.jsx, Unmount.cy.jsx)
│ Specs: 7 found (App.cy.jsx, AppCompilationError.cy.jsx, Errors.cy.jsx, MissingReact.cy.js
x, MissingReactInSpec.cy.jsx, Rerendering.cy.jsx, Unmount.cy.jsx) │
│ Searched: **/*.cy.{js,jsx,ts,tsx} │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: App.cy.jsx (1 of 6)
Running: App.cy.jsx (1 of 7)
✓ renders hello world
@@ -49,7 +49,7 @@ exports['@cypress/webpack-dev-server react executes all of the tests for webpack
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: AppCompilationError.cy.jsx (2 of 6)
Running: AppCompilationError.cy.jsx (2 of 7)
1) An uncaught error was detected outside of a test
@@ -109,7 +109,89 @@ We dynamically generated a new test to display this failure.
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReact.cy.jsx (3 of 6)
Running: Errors.cy.jsx (3 of 7)
Errors
1) error on mount
2) sync error
3) async error
4) command failure
0 passing
4 failing
1) Errors
error on mount:
Error: The following error originated from your test code, not from Cypress.
> mount error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
2) Errors
sync error:
Error: The following error originated from your test code, not from Cypress.
> sync error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
3) Errors
async error:
Error: The following error originated from your test code, not from Cypress.
> async error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
4) Errors
command failure:
AssertionError: Timed out retrying after 50ms: Expected to find element: \`element-that-does-not-exist\`, but never found it.
[stack trace lines]
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 4 │
│ Passing: 0 │
│ Failing: 4 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 4 │
│ Video: true │
│ Duration: X seconds │
│ Spec Ran: Errors.cy.jsx │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(Screenshots)
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- error on mount (failed) (1280x720)
.png
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- sync error (failed).png (1280x720)
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- async error (failed).pn (1280x720)
g
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- command failure (failed (1280x720)
).png
(Video)
- Started processing: Compressing to 32 CRF
- Finished processing: /XXX/XXX/XXX/cypress/videos/Errors.cy.jsx.mp4 (X second)
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReact.cy.jsx (4 of 7)
1) is missing React
@@ -157,7 +239,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReactInSpec.cy.jsx (4 of 6)
Running: MissingReactInSpec.cy.jsx (5 of 7)
1) is missing React in this file
@@ -201,7 +283,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: Rerendering.cy.jsx (5 of 6)
Running: Rerendering.cy.jsx (6 of 7)
re-render
@@ -234,7 +316,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: Unmount.cy.jsx (6 of 6)
Running: Unmount.cy.jsx (7 of 7)
Comp with componentWillUnmount
@@ -280,6 +362,8 @@ When Cypress detects uncaught errors originating from your test code it will aut
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ AppCompilationError.cy.jsx XX:XX 1 - 1 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ Errors.cy.jsx XX:XX 4 - 4 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ MissingReact.cy.jsx XX:XX 1 - 1 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ MissingReactInSpec.cy.jsx XX:XX 1 - 1 - - │
@@ -288,7 +372,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✔ Unmount.cy.jsx XX:XX 3 3 - - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
3 of 6 failed (50%) XX:XX 8 5 3 - -
4 of 7 failed (57%) XX:XX 12 5 7 - -
`
@@ -302,16 +386,16 @@ exports['@cypress/webpack-dev-server react executes all of the tests for webpack
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 1.2.3 │
│ Browser: FooBrowser 88 │
│ Specs: 6 found (App.cy.jsx, AppCompilationError.cy.jsx, MissingReact.cy.jsx, MissingReact │
│ InSpec.cy.jsx, Rerendering.cy.jsx, Unmount.cy.jsx)
│ Specs: 7 found (App.cy.jsx, AppCompilationError.cy.jsx, Errors.cy.jsx, MissingReact.cy.js
x, MissingReactInSpec.cy.jsx, Rerendering.cy.jsx, Unmount.cy.jsx) │
│ Searched: **/*.cy.{js,jsx,ts,tsx} │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: App.cy.jsx (1 of 6)
44 modules
Running: App.cy.jsx (1 of 7)
45 modules
ERROR in ./src/AppCompilationError.cy.jsx
Module build failed (from [..]):
@@ -353,7 +437,7 @@ SyntaxError: /foo/bar/.projects/webpack4_wds4-react/src/AppCompilationError.cy.j
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: AppCompilationError.cy.jsx (2 of 6)
Running: AppCompilationError.cy.jsx (2 of 7)
1) An uncaught error was detected outside of a test
@@ -413,7 +497,89 @@ We dynamically generated a new test to display this failure.
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReact.cy.jsx (3 of 6)
Running: Errors.cy.jsx (3 of 7)
Errors
1) error on mount
2) sync error
3) async error
4) command failure
0 passing
4 failing
1) Errors
error on mount:
Error: The following error originated from your test code, not from Cypress.
> mount error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
2) Errors
sync error:
Error: The following error originated from your test code, not from Cypress.
> sync error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
3) Errors
async error:
Error: The following error originated from your test code, not from Cypress.
> async error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
4) Errors
command failure:
AssertionError: Timed out retrying after 50ms: Expected to find element: \`element-that-does-not-exist\`, but never found it.
[stack trace lines]
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 4 │
│ Passing: 0 │
│ Failing: 4 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 4 │
│ Video: true │
│ Duration: X seconds │
│ Spec Ran: Errors.cy.jsx │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(Screenshots)
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- error on mount (failed) (1280x720)
.png
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- sync error (failed).png (1280x720)
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- async error (failed).pn (1280x720)
g
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- command failure (failed (1280x720)
).png
(Video)
- Started processing: Compressing to 32 CRF
- Finished processing: /XXX/XXX/XXX/cypress/videos/Errors.cy.jsx.mp4 (X second)
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReact.cy.jsx (4 of 7)
1) is missing React
@@ -461,7 +627,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReactInSpec.cy.jsx (4 of 6)
Running: MissingReactInSpec.cy.jsx (5 of 7)
1) is missing React in this file
@@ -505,7 +671,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: Rerendering.cy.jsx (5 of 6)
Running: Rerendering.cy.jsx (6 of 7)
re-render
@@ -538,7 +704,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: Unmount.cy.jsx (6 of 6)
Running: Unmount.cy.jsx (7 of 7)
Comp with componentWillUnmount
@@ -584,6 +750,8 @@ When Cypress detects uncaught errors originating from your test code it will aut
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ AppCompilationError.cy.jsx XX:XX 1 - 1 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ Errors.cy.jsx XX:XX 4 - 4 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ MissingReact.cy.jsx XX:XX 1 - 1 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ MissingReactInSpec.cy.jsx XX:XX 1 - 1 - - │
@@ -592,7 +760,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✔ Unmount.cy.jsx XX:XX 3 3 - - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
3 of 6 failed (50%) XX:XX 8 5 3 - -
4 of 7 failed (57%) XX:XX 12 5 7 - -
`
@@ -609,15 +777,15 @@ exports['@cypress/webpack-dev-server react executes all of the tests for webpack
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 1.2.3 │
│ Browser: FooBrowser 88 │
│ Specs: 6 found (App.cy.jsx, AppCompilationError.cy.jsx, MissingReact.cy.jsx, MissingReact │
│ InSpec.cy.jsx, Rerendering.cy.jsx, Unmount.cy.jsx)
│ Specs: 7 found (App.cy.jsx, AppCompilationError.cy.jsx, Errors.cy.jsx, MissingReact.cy.js
x, MissingReactInSpec.cy.jsx, Rerendering.cy.jsx, Unmount.cy.jsx) │
│ Searched: **/*.cy.{js,jsx,ts,tsx} │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: App.cy.jsx (1 of 6)
Running: App.cy.jsx (1 of 7)
✓ renders hello world
@@ -648,7 +816,7 @@ exports['@cypress/webpack-dev-server react executes all of the tests for webpack
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: AppCompilationError.cy.jsx (2 of 6)
Running: AppCompilationError.cy.jsx (2 of 7)
1) An uncaught error was detected outside of a test
@@ -708,7 +876,89 @@ We dynamically generated a new test to display this failure.
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReact.cy.jsx (3 of 6)
Running: Errors.cy.jsx (3 of 7)
Errors
1) error on mount
2) sync error
3) async error
4) command failure
0 passing
4 failing
1) Errors
error on mount:
Error: The following error originated from your test code, not from Cypress.
> mount error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
2) Errors
sync error:
Error: The following error originated from your test code, not from Cypress.
> sync error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
3) Errors
async error:
Error: The following error originated from your test code, not from Cypress.
> async error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
4) Errors
command failure:
AssertionError: Timed out retrying after 50ms: Expected to find element: \`element-that-does-not-exist\`, but never found it.
[stack trace lines]
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 4 │
│ Passing: 0 │
│ Failing: 4 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 4 │
│ Video: true │
│ Duration: X seconds │
│ Spec Ran: Errors.cy.jsx │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(Screenshots)
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- error on mount (failed) (1280x720)
.png
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- sync error (failed).png (1280x720)
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- async error (failed).pn (1280x720)
g
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- command failure (failed (1280x720)
).png
(Video)
- Started processing: Compressing to 32 CRF
- Finished processing: /XXX/XXX/XXX/cypress/videos/Errors.cy.jsx.mp4 (X second)
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReact.cy.jsx (4 of 7)
1) is missing React
@@ -756,7 +1006,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReactInSpec.cy.jsx (4 of 6)
Running: MissingReactInSpec.cy.jsx (5 of 7)
1) is missing React in this file
@@ -800,7 +1050,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: Rerendering.cy.jsx (5 of 6)
Running: Rerendering.cy.jsx (6 of 7)
re-render
@@ -833,7 +1083,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: Unmount.cy.jsx (6 of 6)
Running: Unmount.cy.jsx (7 of 7)
Comp with componentWillUnmount
@@ -879,6 +1129,8 @@ When Cypress detects uncaught errors originating from your test code it will aut
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ AppCompilationError.cy.jsx XX:XX 1 - 1 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ Errors.cy.jsx XX:XX 4 - 4 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ MissingReact.cy.jsx XX:XX 1 - 1 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ MissingReactInSpec.cy.jsx XX:XX 1 - 1 - - │
@@ -887,7 +1139,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✔ Unmount.cy.jsx XX:XX 3 3 - - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
3 of 6 failed (50%) XX:XX 8 5 3 - -
4 of 7 failed (57%) XX:XX 12 5 7 - -
`
@@ -901,17 +1153,17 @@ exports['@cypress/webpack-dev-server react executes all of the tests for webpack
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 1.2.3 │
│ Browser: FooBrowser 88 │
│ Specs: 6 found (App.cy.jsx, AppCompilationError.cy.jsx, MissingReact.cy.jsx, MissingReact │
│ InSpec.cy.jsx, Rerendering.cy.jsx, Unmount.cy.jsx)
│ Specs: 7 found (App.cy.jsx, AppCompilationError.cy.jsx, Errors.cy.jsx, MissingReact.cy.js
x, MissingReactInSpec.cy.jsx, Rerendering.cy.jsx, Unmount.cy.jsx) │
│ Searched: **/*.cy.{js,jsx,ts,tsx} │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: App.cy.jsx (1 of 6)
13 assets
54 modules
Running: App.cy.jsx (1 of 7)
14 assets
55 modules
ERROR in ./src/AppCompilationError.cy.jsx
Module build failed (from [..]):
@@ -955,7 +1207,7 @@ webpack x.x.x compiled with x errors in xxx ms
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: AppCompilationError.cy.jsx (2 of 6)
Running: AppCompilationError.cy.jsx (2 of 7)
1) An uncaught error was detected outside of a test
@@ -1015,7 +1267,89 @@ We dynamically generated a new test to display this failure.
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReact.cy.jsx (3 of 6)
Running: Errors.cy.jsx (3 of 7)
Errors
1) error on mount
2) sync error
3) async error
4) command failure
0 passing
4 failing
1) Errors
error on mount:
Error: The following error originated from your test code, not from Cypress.
> mount error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
2) Errors
sync error:
Error: The following error originated from your test code, not from Cypress.
> sync error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
3) Errors
async error:
Error: The following error originated from your test code, not from Cypress.
> async error
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
[stack trace lines]
4) Errors
command failure:
AssertionError: Timed out retrying after 50ms: Expected to find element: \`element-that-does-not-exist\`, but never found it.
[stack trace lines]
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 4 │
│ Passing: 0 │
│ Failing: 4 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 4 │
│ Video: true │
│ Duration: X seconds │
│ Spec Ran: Errors.cy.jsx │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(Screenshots)
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- error on mount (failed) (1280x720)
.png
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- sync error (failed).png (1280x720)
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- async error (failed).pn (1280x720)
g
- /XXX/XXX/XXX/cypress/screenshots/Errors.cy.jsx/Errors -- command failure (failed (1280x720)
).png
(Video)
- Started processing: Compressing to 32 CRF
- Finished processing: /XXX/XXX/XXX/cypress/videos/Errors.cy.jsx.mp4 (X second)
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReact.cy.jsx (4 of 7)
1) is missing React
@@ -1063,7 +1397,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: MissingReactInSpec.cy.jsx (4 of 6)
Running: MissingReactInSpec.cy.jsx (5 of 7)
1) is missing React in this file
@@ -1107,7 +1441,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: Rerendering.cy.jsx (5 of 6)
Running: Rerendering.cy.jsx (6 of 7)
re-render
@@ -1140,7 +1474,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: Unmount.cy.jsx (6 of 6)
Running: Unmount.cy.jsx (7 of 7)
Comp with componentWillUnmount
@@ -1186,6 +1520,8 @@ When Cypress detects uncaught errors originating from your test code it will aut
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ AppCompilationError.cy.jsx XX:XX 1 - 1 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ Errors.cy.jsx XX:XX 4 - 4 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ MissingReact.cy.jsx XX:XX 1 - 1 - - │
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✖ MissingReactInSpec.cy.jsx XX:XX 1 - 1 - - │
@@ -1194,7 +1530,7 @@ When Cypress detects uncaught errors originating from your test code it will aut
├────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ✔ Unmount.cy.jsx XX:XX 3 3 - - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
3 of 6 failed (50%) XX:XX 8 5 3 - -
4 of 7 failed (57%) XX:XX 12 5 7 - -
`

View File

@@ -0,0 +1,22 @@
import { Component, Input } from "@angular/core";
@Component({
selector: "errors-component",
template: `<div>
<button id="sync-error" (click)="syncError()">Sync Error</button>
<button id="async-error" (click)="asyncError()">Sync Error</button>
</div>`,
})
export class ErrorsComponent {
@Input() throwError!: boolean;
syncError() {
throw new Error('sync error')
}
asyncError() {
setTimeout(() => {
throw new Error('async error')
}, 50)
}
}

View File

@@ -0,0 +1,23 @@
import { ErrorsComponent } from './components/errors'
describe('Errors', () => {
it('error on mount', () => {
cy.mount(ErrorsComponent, { componentProperties: { throwError: true } })
})
it('sync error', () => {
cy.mount(ErrorsComponent)
cy.get('#sync-error').click()
})
it('async error', () => {
cy.mount(ErrorsComponent)
cy.get('#async-error').click()
})
it('command failure', { defaultCommandTimeout: 50 }, () => {
cy.mount(ErrorsComponent)
cy.get('element-that-does-not-exist')
})
})

View File

@@ -0,0 +1,46 @@
import React from 'react'
import { mount } from "cypress/react"
describe('Errors', () => {
const Errors = (props) => {
if (props.throwError) throw new Error('mount error')
return (
<div>
<button
id="sync-error"
onClick={() => { throw new Error('sync error') }}
>
Sync Error
</button>
<button
id="async-error"
onClick={() => {
setTimeout(() => { throw new Error('async error') }, 50)
}}
>
Async Error
</button>
</div>
);
}
it('error on mount', () => {
mount(<Errors throwError />)
})
it('sync error', () => {
mount(<Errors />)
cy.get('#sync-error').click()
})
it('async error', () => {
mount(<Errors />)
cy.get('#async-error').click()
})
it('command failure', { defaultCommandTimeout: 50 }, () => {
mount(<Errors />)
cy.get('element-that-does-not-exist')
})
})

View File

@@ -0,0 +1,45 @@
import React from 'react'
describe('Errors', () => {
const Errors = (props) => {
if (props.throwError) throw new Error('mount error')
return (
<div>
<button
id="sync-error"
onClick={() => { throw new Error('sync error') }}
>
Sync Error
</button>
<button
id="async-error"
onClick={() => {
setTimeout(() => { throw new Error('async error') }, 50)
}}
>
Async Error
</button>
</div>
);
}
it('error on mount', () => {
cy.mount(<Errors throwError />)
})
it('sync error', () => {
cy.mount(<Errors />)
cy.get('#sync-error').click()
})
it('async error', () => {
cy.mount(<Errors />)
cy.get('#async-error').click()
})
it('command failure', { defaultCommandTimeout: 50 }, () => {
cy.mount(<Errors />)
cy.get('element-that-does-not-exist')
})
})

View File

@@ -1,18 +1,18 @@
describe('event handlers', { defaultCommandTimeout: 0 }, () => {
it('event assertion failure', () => {
cy.on('window:load', () => {
cy.on('command:end', () => {
expect('actual').to.equal('expected')
})
cy.visit('http://localhost:1919')
cy.wrap({})
})
it('event exception', () => {
cy.on('window:load', () => {
cy.on('command:end', () => {
({}).bar()
})
cy.visit('http://localhost:1919')
cy.wrap({})
})
it('fail handler assertion failure', () => {

View File

@@ -0,0 +1,75 @@
import Errors from '../../../src/Errors'
import Bluebird from 'bluebird'
import React from 'react'
describe('uncaught errors', { defaultCommandTimeout: 0 }, () => {
it('sync app mount exception', () => {
cy.mount(React.createElement(Errors, { throwOnMount: true }, []))
})
it('sync app exception after mount', () => {
cy.mount(React.createElement(Errors, null, []))
cy.get('#trigger-sync-error').click()
})
it('async app exception after mount', () => {
cy.mount(React.createElement(Errors, null, []))
cy.get('#trigger-async-error').click()
cy.wait(10000)
})
it('app unhandled rejection', () => {
cy.mount(React.createElement(Errors, null, []))
cy.get('#trigger-unhandled-rejection').click()
cy.wait(10000)
})
it('exception inside uncaught:exception', () => {
cy.on('uncaught:exception', () => {
({}).bar()
})
cy.mount(React.createElement(Errors, { throwOnMount: true }, []))
})
it('async spec exception', () => {
setTimeout(() => {
({}).bar()
})
cy.wait(10000)
})
// eslint-disable-next-line mocha/handle-done-callback
it('async spec exception with done', (done) => {
setTimeout(() => {
({}).bar()
})
cy.wait(50)
})
it('spec unhandled rejection', () => {
Promise.reject(new Error('Unhandled promise rejection from the spec'))
cy.wait(10000)
})
// eslint-disable-next-line mocha/handle-done-callback
it('spec unhandled rejection with done', (done) => {
Promise.reject(new Error('Unhandled promise rejection from the spec'))
cy.wait(50)
})
it('spec Bluebird unhandled rejection', () => {
Bluebird.reject(new Error('Unhandled promise rejection from the spec'))
cy.wait(10000)
})
// eslint-disable-next-line mocha/handle-done-callback
it('spec Bluebird unhandled rejection with done', (done) => {
Bluebird.reject(new Error('Unhandled promise rejection from the spec'))
})
})

Some files were not shown because too many files have changed in this diff Show More