mirror of
https://github.com/cypress-io/cypress.git
synced 2026-04-24 07:59:12 -05:00
fix: Correctly catch and clear errors during resetErrorAndLoadConfig mutation (#22514)
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
<BaseError
|
||||
v-if="query.data.value?.baseError"
|
||||
:gql="query.data.value.baseError"
|
||||
:retry="resetErrorAndLoadConfig"
|
||||
@retry="resetErrorAndLoadConfig"
|
||||
/>
|
||||
<div v-else>
|
||||
<Spinner />
|
||||
|
||||
@@ -22,7 +22,7 @@ export class ErrorActions {
|
||||
}
|
||||
|
||||
if (d.baseError?.id === id) {
|
||||
d.baseError === null
|
||||
d.baseError = null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -797,7 +797,7 @@ export class ProjectLifecycleManager {
|
||||
* centrally in the e2e tests, as well as notify the "pending initialization"
|
||||
* for run mode
|
||||
*/
|
||||
private onLoadError = (err: any) => {
|
||||
onLoadError = (err: CypressError) => {
|
||||
if (this.ctx.isRunMode && this._configManager) {
|
||||
this._configManager.onLoadError(err)
|
||||
} else {
|
||||
|
||||
@@ -237,7 +237,6 @@ function startAppServer (mode: 'component' | 'e2e' = 'e2e', options: { skipMocki
|
||||
const isInitialized = o.pDefer()
|
||||
const initializeActive = ctx.actions.project.initializeActiveProject
|
||||
const onErrorStub = o.sinon.stub(ctx, 'onError')
|
||||
// @ts-expect-error - errors b/c it's a private method
|
||||
const onLoadErrorStub = o.sinon.stub(ctx.lifecycleManager, 'onLoadError')
|
||||
const initializeActiveProjectStub = o.sinon.stub(ctx.actions.project, 'initializeActiveProject')
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ const props = defineProps<{
|
||||
suffixIcon?: FunctionalComponent<SVGAttributes>
|
||||
size?: ButtonSizes
|
||||
variant?: ButtonVariants
|
||||
prefixIconClass?: string
|
||||
prefixIconClass?: string | Record<string, string | boolean>
|
||||
suffixIconClass?: string
|
||||
href?: string // will cause the button to render as link element with button styles
|
||||
to?: object | string // will render as a router-link with button styles
|
||||
|
||||
@@ -21,7 +21,7 @@ describe('<BaseError />', () => {
|
||||
|
||||
it('renders the default error the correct messages', () => {
|
||||
cy.mountFragment(BaseErrorFragmentDoc, {
|
||||
render: (gqlVal) => <BaseError gql={gqlVal} />,
|
||||
render: (gqlVal) => <BaseError gql={gqlVal} showButtons={false} />,
|
||||
})
|
||||
.get(headerSelector)
|
||||
.should('contain.text', cy.gqlStub.ErrorWrapper.title)
|
||||
@@ -31,7 +31,7 @@ describe('<BaseError />', () => {
|
||||
.should('not.exist')
|
||||
})
|
||||
|
||||
context('retry prop', () => {
|
||||
context('retry action', () => {
|
||||
const { docsButton } = cy.i18n.launchpadErrors.generic
|
||||
|
||||
const mountFragmentWithError = (errorProps = {}) => {
|
||||
@@ -41,7 +41,7 @@ describe('<BaseError />', () => {
|
||||
render: (gqlVal) => (<BaseError gql={{
|
||||
...gqlVal,
|
||||
...errorProps,
|
||||
}} retry={retrySpy} />),
|
||||
}} onRetry={retrySpy} />),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -69,18 +69,19 @@ describe('<BaseError />', () => {
|
||||
.should('have.attr', 'href', docsButton.configGuide.link)
|
||||
})
|
||||
|
||||
it('calls the retry function passed in', () => {
|
||||
it(`emits a 'retry' event when clicked`, () => {
|
||||
mountFragmentWithError()
|
||||
cy.get(retryButtonSelector)
|
||||
.should('not.be.disabled')
|
||||
.click()
|
||||
.click()
|
||||
.get('@retry')
|
||||
.should('have.been.calledTwice')
|
||||
})
|
||||
|
||||
it('does not render retry or docs buttons when retry prop is NOT passed in', () => {
|
||||
it('does not render retry or docs buttons when showButtons prop is false', () => {
|
||||
cy.mountFragment(BaseErrorFragmentDoc, {
|
||||
render: (gqlVal) => <BaseError gql={gqlVal} />,
|
||||
render: (gqlVal) => <BaseError gql={gqlVal} showButtons={false} />,
|
||||
})
|
||||
|
||||
cy.get(retryButtonSelector).should('not.exist')
|
||||
|
||||
@@ -16,15 +16,15 @@
|
||||
</h1>
|
||||
|
||||
<div
|
||||
v-if="retry"
|
||||
class="font-medium w-full inline-flex pt-12px justify-center gap-4 "
|
||||
v-if="showButtons"
|
||||
class="font-medium w-full pt-12px gap-4 inline-flex justify-center "
|
||||
>
|
||||
<Button
|
||||
variant="outline"
|
||||
data-testid="error-retry-button"
|
||||
:prefix-icon="RestartIcon"
|
||||
prefix-icon-class="icon-dark-indigo-500"
|
||||
@click="retry?.(baseError.id)"
|
||||
@click="emit('retry', baseError.id)"
|
||||
>
|
||||
{{ t('launchpadErrors.generic.retryButton') }}
|
||||
</Button>
|
||||
@@ -139,9 +139,13 @@ fragment BaseError on ErrorWrapper {
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps<{
|
||||
const props = withDefaults(defineProps<{
|
||||
gql: BaseErrorFragment
|
||||
retry?: (id: string) => void
|
||||
showButtons?: boolean
|
||||
}>(), { showButtons: true })
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'retry', id: string): void
|
||||
}>()
|
||||
|
||||
const markdownTarget = ref()
|
||||
|
||||
@@ -33,7 +33,7 @@ export const mutation = mutationType({
|
||||
},
|
||||
resolve: async (_, args, ctx) => {
|
||||
ctx.actions.error.clearError(args.id)
|
||||
await ctx.lifecycleManager.refreshLifecycle()
|
||||
await ctx.lifecycleManager.refreshLifecycle().catch(ctx.lifecycleManager.onLoadError)
|
||||
|
||||
return {}
|
||||
},
|
||||
|
||||
@@ -147,8 +147,30 @@ describe('Launchpad: Error System Tests', () => {
|
||||
cy.visitLaunchpad()
|
||||
cy.contains('h1', cy.i18n.launchpadErrors.generic.configErrorTitle)
|
||||
cy.percySnapshot()
|
||||
|
||||
cy.withCtx(async (ctx) => {
|
||||
await ctx.actions.file.writeFileInProject('cypress.config.ts', 'module.exports = { e2e: { supportFile: false } }')
|
||||
await ctx.actions.file.writeFileInProject('cypress.config.ts', 'export default { e2e: { supportFile: false } }')
|
||||
})
|
||||
|
||||
cy.findByRole('button', { name: 'Try again' }).click()
|
||||
|
||||
cy.get('h1').should('contain', 'Welcome to Cypress')
|
||||
})
|
||||
|
||||
it(`clears the error correctly after first 'try again' attempt`, () => {
|
||||
cy.scaffoldProject('config-with-ts-syntax-error')
|
||||
cy.openProject('config-with-ts-syntax-error')
|
||||
cy.visitLaunchpad()
|
||||
cy.contains('h1', cy.i18n.launchpadErrors.generic.configErrorTitle)
|
||||
|
||||
// Try again while the config is still invalid
|
||||
cy.findByRole('button', { name: 'Try again' }).click()
|
||||
|
||||
// Wait until config error is on screen again
|
||||
cy.contains('h1', cy.i18n.launchpadErrors.generic.configErrorTitle)
|
||||
|
||||
cy.withCtx(async (ctx) => {
|
||||
await ctx.actions.file.writeFileInProject('cypress.config.ts', 'export default { e2e: { supportFile: false } }')
|
||||
})
|
||||
|
||||
cy.findByRole('button', { name: 'Try again' }).click()
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<BaseError
|
||||
v-if="query.data.value.baseError"
|
||||
:gql="query.data.value.baseError"
|
||||
:retry="resetErrorAndLoadConfig"
|
||||
@retry="resetErrorAndLoadConfig"
|
||||
/>
|
||||
<GlobalPage
|
||||
v-else-if="query.data.value.isInGlobalMode || !query.data.value?.currentProject"
|
||||
|
||||
Reference in New Issue
Block a user