diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 9a7234292a8b..ea2d773caff9 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -1,13 +1,17 @@ ## 12.12.1 -_Released 05/14/2023 (PENDING)_ +_Released 05/23/2023 (PENDING)_ **Bugfixes:** - Reverted [#26452](https://github.com/cypress-io/cypress/pull/26630) which introduced a bug that prevents users from using End to End with Yarn 3. Fixed in [#26735](https://github.com/cypress-io/cypress/pull/26735). Fixes [#26676](https://github.com/cypress-io/cypress/issues/26676). - Moved `types` condition to the front of `package.json#exports` since keys there are meant to be order-sensitive. Fixed in [#26630](https://github.com/cypress-io/cypress/pull/26630). +**Misc:** + +- Updated styling & content of Cypress Cloud slideshows when not logged in or no runs have been recorded. Addresses [#26181](https://github.com/cypress-io/cypress/issues/26181). + ## 12.12.0 _Released 05/09/2023_ diff --git a/packages/app/cypress/e2e/runs.cy.ts b/packages/app/cypress/e2e/runs.cy.ts index f9df449949c4..cdf45b776561 100644 --- a/packages/app/cypress/e2e/runs.cy.ts +++ b/packages/app/cypress/e2e/runs.cy.ts @@ -577,7 +577,7 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { }) }) - context('Runs - No Runs', () => { + context('Runs - No Runs', { viewportWidth: 1280 }, () => { it('when no runs and not connected, shows connect to Cypress Cloud button', () => { cy.scaffoldProject('component-tests') cy.openProject('component-tests', ['--config-file', 'cypressWithoutProjectId.config.js']) @@ -602,7 +602,6 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { it('displays how to record prompt when connected and no runs in Component Testing', () => { scaffoldTestingTypeAndVisitRunsPage('component') cy.contains(defaultMessages.runs.empty.title).should('be.visible') - cy.contains(defaultMessages.runs.empty.description).should('be.visible') cy.findByDisplayValue('npx cypress run --component --record --key 2aaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa').should('be.visible') }) @@ -610,7 +609,6 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { scaffoldTestingTypeAndVisitRunsPage('e2e') cy.contains(defaultMessages.runs.empty.title).should('be.visible') - cy.contains(defaultMessages.runs.empty.description).should('be.visible') cy.findByDisplayValue('npx cypress run --record --key 2aaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa').should('be.visible') }) diff --git a/packages/app/src/assets/debug-guide-skeleton-1.png b/packages/app/src/assets/debug-guide-skeleton-1.png deleted file mode 100644 index 2abb50e0bb50..000000000000 Binary files a/packages/app/src/assets/debug-guide-skeleton-1.png and /dev/null differ diff --git a/packages/app/src/assets/debug-guide-skeleton-1.svg b/packages/app/src/assets/debug-guide-skeleton-1.svg new file mode 100644 index 000000000000..8d5c836e53b4 --- /dev/null +++ b/packages/app/src/assets/debug-guide-skeleton-1.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/app/src/assets/debug-guide-skeleton-2.png b/packages/app/src/assets/debug-guide-skeleton-2.png deleted file mode 100644 index 44d36ccbe60b..000000000000 Binary files a/packages/app/src/assets/debug-guide-skeleton-2.png and /dev/null differ diff --git a/packages/app/src/assets/debug-guide-skeleton-2.svg b/packages/app/src/assets/debug-guide-skeleton-2.svg new file mode 100644 index 000000000000..611e8f2ef594 --- /dev/null +++ b/packages/app/src/assets/debug-guide-skeleton-2.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/app/src/assets/debug-guide-skeleton-3.png b/packages/app/src/assets/debug-guide-skeleton-3.png deleted file mode 100644 index 9b7957130f2f..000000000000 Binary files a/packages/app/src/assets/debug-guide-skeleton-3.png and /dev/null differ diff --git a/packages/app/src/assets/debug-guide-skeleton-3.svg b/packages/app/src/assets/debug-guide-skeleton-3.svg new file mode 100644 index 000000000000..83cc61d919ee --- /dev/null +++ b/packages/app/src/assets/debug-guide-skeleton-3.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/app/src/assets/debug-guide-text-1.png b/packages/app/src/assets/debug-guide-text-1.png deleted file mode 100644 index 0c3dbeecffda..000000000000 Binary files a/packages/app/src/assets/debug-guide-text-1.png and /dev/null differ diff --git a/packages/app/src/assets/debug-guide-text-2.png b/packages/app/src/assets/debug-guide-text-2.png deleted file mode 100644 index 4a7dd371a1cd..000000000000 Binary files a/packages/app/src/assets/debug-guide-text-2.png and /dev/null differ diff --git a/packages/app/src/assets/debug-guide-text-3.png b/packages/app/src/assets/debug-guide-text-3.png deleted file mode 100644 index 3a1928737037..000000000000 Binary files a/packages/app/src/assets/debug-guide-text-3.png and /dev/null differ diff --git a/packages/app/src/assets/record-guide.svg b/packages/app/src/assets/record-guide.svg new file mode 100644 index 000000000000..d0561697a6d8 --- /dev/null +++ b/packages/app/src/assets/record-guide.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/app/src/assets/runs-guide-skeleton-1.svg b/packages/app/src/assets/runs-guide-skeleton-1.svg new file mode 100644 index 000000000000..3ec7a6ad2223 --- /dev/null +++ b/packages/app/src/assets/runs-guide-skeleton-1.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/app/src/assets/runs-guide-skeleton-2.svg b/packages/app/src/assets/runs-guide-skeleton-2.svg new file mode 100644 index 000000000000..566ca8059b33 --- /dev/null +++ b/packages/app/src/assets/runs-guide-skeleton-2.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/app/src/assets/runs-guide-skeleton-3.svg b/packages/app/src/assets/runs-guide-skeleton-3.svg new file mode 100644 index 000000000000..0d439f56cbaa --- /dev/null +++ b/packages/app/src/assets/runs-guide-skeleton-3.svg @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/app/src/components/Slideshow.vue b/packages/app/src/components/Slideshow.vue index 2d08e039316c..b6a34bb04008 100644 --- a/packages/app/src/components/Slideshow.vue +++ b/packages/app/src/components/Slideshow.vue @@ -10,48 +10,33 @@ leave-active-class="transition duration-300 ease-in absolute" leave-to-class="opacity-0 absolute" > - diff --git a/packages/app/src/components/promo/Promo.cy.tsx b/packages/app/src/components/promo/Promo.cy.tsx new file mode 100644 index 000000000000..67f0861726e9 --- /dev/null +++ b/packages/app/src/components/promo/Promo.cy.tsx @@ -0,0 +1,126 @@ +import Promo from './Promo.vue' +import PromoCard from './PromoCard.vue' +import PromoAction from './PromoAction.vue' +import PromoHeader from './PromoHeader.vue' +import { IconActionRestart, IconChevronRightSmall } from '@cypress-design/vue-icon' +import { Promo_PromoSeenDocument } from '../../generated/graphql-test' + +describe('', () => { + const renderPromo = () => { + const recordEvent = cy.stub().as('recordEvent') + + cy.stubMutationResolver(Promo_PromoSeenDocument, (defineResult, args) => { + recordEvent(args) + + return defineResult({ recordEvent: true }) + }) + + cy.mount( + + {{ + header: () => ( + + {{ + description: () => ( +

Description...

+ ), + content: () => ( +

_content_

+ ), + }} +
+ ), + cards: ({ step, goForward, reset }) => { + if (step === 0) { + return ( + + {{ + image: () => ( +
Image
+ ), + action: () => ( + + ), + }} +
+ ) + } + + if (step === 1) { + return ( + + {{ + image: () => ( +
Image
+ ), + action: () => ( + + ), + }} +
+ ) + } + + return null + }, + }} +
, + ) + } + + beforeEach(() => { + renderPromo() + }) + + it('renders properly on narrow viewport', { viewportWidth: 600 }, () => { + cy.percySnapshot() + }) + + it('renders properly on wide viewport', { viewportWidth: 1280 }, () => { + cy.percySnapshot() + }) + + it('records event on initial render', () => { + cy.get('@recordEvent').should('have.been.calledOnce') + cy.get('@recordEvent').should('have.been.calledWith', { + campaign: '_campaign_', + medium: '_medium_', + cohort: null, + messageId: Cypress.sinon.match.string, + }) + }) + + describe('controls', () => { + it('handles `goForward`', () => { + cy.contains('Card #1').should('be.visible') + cy.findByTestId('promo-action-control').click() + cy.contains('Card #2').should('be.visible') + }) + + it('handles `reset`', () => { + cy.findByTestId('promo-action-control').click() + cy.contains('Card #2').should('be.visible') + cy.findByTestId('promo-action-control').should('contain.text', 'Reset') + cy.findByTestId('promo-action-control').click() + cy.contains('Card #1').should('be.visible') + }) + }) +}) diff --git a/packages/app/src/components/promo/Promo.vue b/packages/app/src/components/promo/Promo.vue new file mode 100644 index 000000000000..815e078222c8 --- /dev/null +++ b/packages/app/src/components/promo/Promo.vue @@ -0,0 +1,71 @@ + + + diff --git a/packages/app/src/components/promo/PromoAction.cy.tsx b/packages/app/src/components/promo/PromoAction.cy.tsx new file mode 100644 index 000000000000..c22a1b53ebcb --- /dev/null +++ b/packages/app/src/components/promo/PromoAction.cy.tsx @@ -0,0 +1,43 @@ +import PromoAction from './PromoAction.vue' +import { IconChevronRightSmall, IconActionRestart } from '@cypress-design/vue-icon' + +describe('', () => { + it('left label, right icon', () => { + const action = cy.stub().as('action') + + cy.mount( + , + ) + + cy.get('@action').should('not.have.been.called') + + cy.findByTestId('promo-action-control').click() + + cy.get('@action').should('have.been.calledOnce') + }) + + it('left icon, right label', () => { + const action = cy.stub().as('action') + + cy.mount( + , + ) + + cy.get('@action').should('not.have.been.called') + + cy.findByTestId('promo-action-control').click() + + cy.get('@action').should('have.been.calledOnce') + }) +}) diff --git a/packages/app/src/components/promo/PromoAction.vue b/packages/app/src/components/promo/PromoAction.vue new file mode 100644 index 000000000000..21cad8824d85 --- /dev/null +++ b/packages/app/src/components/promo/PromoAction.vue @@ -0,0 +1,47 @@ + + + diff --git a/packages/app/src/components/promo/PromoCard.vue b/packages/app/src/components/promo/PromoCard.vue new file mode 100644 index 000000000000..c7e2894bae37 --- /dev/null +++ b/packages/app/src/components/promo/PromoCard.vue @@ -0,0 +1,27 @@ + + + diff --git a/packages/app/src/components/promo/PromoHeader.cy.tsx b/packages/app/src/components/promo/PromoHeader.cy.tsx new file mode 100644 index 000000000000..802cab83e5b8 --- /dev/null +++ b/packages/app/src/components/promo/PromoHeader.cy.tsx @@ -0,0 +1,17 @@ +import PromoHeader from './PromoHeader.vue' +import Button from '@cypress-design/vue-button' + +describe('', () => { + it('renders', () => { + cy.mount( +

Description of this header

, + control: () => , + content: () =>
Test Content
, + }} + />, + ) + }) +}) diff --git a/packages/app/src/components/promo/PromoHeader.vue b/packages/app/src/components/promo/PromoHeader.vue new file mode 100644 index 000000000000..01c41a3f0ec2 --- /dev/null +++ b/packages/app/src/components/promo/PromoHeader.vue @@ -0,0 +1,43 @@ + + + diff --git a/packages/app/src/debug/DebugContainer.cy.tsx b/packages/app/src/debug/DebugContainer.cy.tsx index 3b1511300eb0..9b8f30362d8c 100644 --- a/packages/app/src/debug/DebugContainer.cy.tsx +++ b/packages/app/src/debug/DebugContainer.cy.tsx @@ -4,7 +4,6 @@ import { defaultMessages } from '@cy/i18n' import { useUserProjectStatusStore } from '@packages/frontend-shared/src/store/user-project-status-store' import { specsList } from './utils/DebugMapping' import { CloudRunStubs, createCloudRun } from '@packages/graphql/test/stubCloudTypes' -import { DEBUG_SLIDESHOW } from './utils/constants' import type { CloudRun, CloudSpecRun, CloudTestResult } from '@packages/graphql/src/gen/test-cloud-graphql-types.gen' const DebugSpecVariableTypes = { @@ -29,19 +28,12 @@ describe('', () => { describe('empty states', () => { const validateEmptyState = (expectedMessages: string[]) => { cy.stubMutationResolver(UseCohorts_DetermineCohortDocument, (defineResult) => { - return defineResult({ determineCohort: { __typename: 'Cohort', name: DEBUG_SLIDESHOW.id, cohort: 'A' } }) + return defineResult({ determineCohort: { __typename: 'Cohort', name: 'iatr_debug_slideshow', cohort: 'A' } }) }) cy.mountFragment(DebugSpecsFragmentDoc, { variableTypes: DebugSpecVariableTypes, variables: defaultVariables, - onResult: (res) => { - if (res.currentProject) { - res.currentProject.savedState = { - debugSlideshowComplete: true, - } - } - }, render: (gqlVal) => , }) @@ -55,7 +47,7 @@ describe('', () => { userProjectStatusStore.setHasInitiallyLoaded() - validateEmptyState([defaultMessages.debugPage.emptyStates.connectToCypressCloud, defaultMessages.debugPage.emptyStates.debugDirectlyInCypress, defaultMessages.debugPage.emptyStates.notLoggedInTestMessage]) + validateEmptyState([defaultMessages.debugPage.emptyStates.connectToCypressCloud, defaultMessages.debugPage.emptyStates.connect.title, defaultMessages.debugPage.emptyStates.connect.description]) cy.findByRole('button', { name: 'Connect to Cypress Cloud' }).should('be.visible') }) @@ -66,7 +58,7 @@ describe('', () => { userProjectStatusStore.setProjectFlag('isProjectConnected', false) userProjectStatusStore.setHasInitiallyLoaded() - validateEmptyState([defaultMessages.debugPage.emptyStates.debugDirectlyInCypress, defaultMessages.debugPage.emptyStates.reviewRerunAndDebug, defaultMessages.debugPage.emptyStates.noProjectTestMessage]) + validateEmptyState([defaultMessages.debugPage.emptyStates.connect.title, defaultMessages.debugPage.emptyStates.connect.description]) cy.findByRole('button', { name: 'Connect a Cypress Cloud project' }).should('be.visible') }) @@ -82,7 +74,7 @@ describe('', () => { render: (gqlVal) => , }) - validateEmptyState([defaultMessages.debugPage.emptyStates.recordYourFirstRun, defaultMessages.debugPage.emptyStates.almostThere, defaultMessages.debugPage.emptyStates.noRunsTestMessage]) + validateEmptyState([defaultMessages.debugPage.emptyStates.noRuns.title]) cy.findByDisplayValue('npx cypress run --record --key 2aaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa').should('be.visible') }) diff --git a/packages/app/src/debug/DebugContainer.vue b/packages/app/src/debug/DebugContainer.vue index 7b9e722e9dac..94072128c7a7 100644 --- a/packages/app/src/debug/DebugContainer.vue +++ b/packages/app/src/debug/DebugContainer.vue @@ -184,7 +184,6 @@ fragment DebugSpecs on Query { } currentTestingType } - ..._DebugEmptyView } ` diff --git a/packages/app/src/debug/empty/DebugEmptyStates.cy.tsx b/packages/app/src/debug/empty/DebugEmptyStates.cy.tsx index 6dd4c6aa621c..20f1acb8a0c2 100644 --- a/packages/app/src/debug/empty/DebugEmptyStates.cy.tsx +++ b/packages/app/src/debug/empty/DebugEmptyStates.cy.tsx @@ -5,73 +5,39 @@ import DebugLoading from './DebugLoading.vue' import DebugError from './DebugError.vue' import DebugEmptyView from './DebugEmptyView.vue' import { useUserProjectStatusStore } from '@packages/frontend-shared/src/store/user-project-status-store' -import { DebugEmptyView_RecordEventDocument, DebugEmptyView_SetPreferencesDocument, UseCohorts_DetermineCohortDocument, _DebugEmptyViewFragment, _DebugEmptyViewFragmentDoc } from '../../generated/graphql-test' -import { DEBUG_SLIDESHOW } from '../utils/constants' - -function mountWithGql (component: JSX.Element, gqlOptions?: { debugSlideshowComplete?: boolean, cohort?: 'A' | 'B' }) { - let gql: _DebugEmptyViewFragment - const opts = { debugSlideshowComplete: true, cohort: 'A', ...gqlOptions } - - cy.stubMutationResolver(UseCohorts_DetermineCohortDocument, (defineResult) => { - return defineResult({ determineCohort: { __typename: 'Cohort', name: DEBUG_SLIDESHOW.id, cohort: opts.cohort } }) - }) +import { Promo_PromoSeenDocument, _PromoFragmentDoc } from '../../generated/graphql-test' +import { DEBUG_PROMO_CAMPAIGNS, DEBUG_TAB_MEDIUM } from '../utils/constants' +function mountWithGql (component: JSX.Element) { const recordEvent = cy.stub().as('recordEvent') - const storeSlideshowComplete = cy.stub().as('storeSlideshowComplete') - cy.stubMutationResolver(DebugEmptyView_RecordEventDocument, (defineResult, args) => { + cy.stubMutationResolver(Promo_PromoSeenDocument, (defineResult, args) => { recordEvent(args) return defineResult({ recordEvent: true }) }) - cy.stubMutationResolver(DebugEmptyView_SetPreferencesDocument, (defineResult, args) => { - storeSlideshowComplete(args) - - return defineResult({ - setPreferences: { - __typename: 'Query', - currentProject: { - __typename: 'CurrentProject', - id: gql.currentProject?.id!, - savedState: { - debugSlideshowComplete: true, - }, - }, - }, - }) - }) - - cy.mountFragment(_DebugEmptyViewFragmentDoc, { - onResult: (res) => { - if (res.currentProject) { - res.currentProject.savedState = { - debugSlideshowComplete: opts.debugSlideshowComplete, - } - - gql = res - } - }, + cy.mountFragment(_PromoFragmentDoc, { render: () => { return component }, }) } -describe('Debug page empty states', () => { +describe('Debug page empty states', { defaultCommandTimeout: 250 }, () => { context('empty view', () => { it('renders with slot', () => { const slotVariableStub = cy.stub().as('slot') mountWithGql( - + {{ cta: slotVariableStub, }} , ) - cy.get('@slot').should('be.calledWith', { utmContent: Cypress.sinon.match.string }) + cy.get('@slot').should('be.calledWith', { utmContent: 'Z' }) }) }) @@ -84,15 +50,41 @@ describe('Debug page empty states', () => { mountWithGql() - cy.findByRole('link', { name: 'Learn more about debugging CI failures in Cypress' }).should('have.attr', 'href', 'https://on.cypress.io/debug-page?utm_source=Binary%3A+Launchpad&utm_medium=Debug+Tab&utm_campaign=Learn+More') - cy.percySnapshot() }) - it('sends record event upon seeing slideshow', () => { - useUserProjectStatusStore().setUserFlag('isLoggedIn', false) - mountWithGql(, { debugSlideshowComplete: false }) - cy.get('@recordEvent').should('have.been.calledWithMatch', { campaign: DEBUG_SLIDESHOW.campaigns.login, messageId: Cypress.sinon.match.string, medium: DEBUG_SLIDESHOW.medium, cohort: Cypress.sinon.match(/A|B/) }) + context('slideshow', () => { + function moveThroughSlideshow (options: { percy?: boolean }) { + for (const step of [1, 2, 3]) { + const { title, description } = cy.i18n.debugPage.emptyStates.slideshow[`step${step}`] + + cy.contains(title) + cy.contains(description) + if (options.percy) { + cy.percySnapshot(`slideshow step ${step}`) + } + + cy.findByTestId('promo-action-control').click() + } + } + + it('renders slideshow', () => { + useUserProjectStatusStore().setUserFlag('isLoggedIn', false) + mountWithGql() + cy.get('@recordEvent').should('have.been.calledWithMatch', { campaign: DEBUG_PROMO_CAMPAIGNS.login, messageId: Cypress.sinon.match.string, medium: DEBUG_TAB_MEDIUM, cohort: null }) + moveThroughSlideshow({ percy: true }) + + // Can repeat moving through slideshow once complete + // Should not re-record slideshow being seen + cy.get('@recordEvent').should('not.have.been.calledTwice') + moveThroughSlideshow({ }) + }) + + it('sends record event upon seeing slideshow', () => { + useUserProjectStatusStore().setUserFlag('isLoggedIn', false) + mountWithGql() + cy.get('@recordEvent').should('have.been.calledWithMatch', { campaign: DEBUG_PROMO_CAMPAIGNS.login, messageId: Cypress.sinon.match.string, medium: DEBUG_TAB_MEDIUM, cohort: null }) + }) }) }) @@ -105,19 +97,7 @@ describe('Debug page empty states', () => { mountWithGql() - cy.findByRole('link', { name: 'Learn more about project setup in Cypress' }).should('have.attr', 'href', 'https://on.cypress.io/adding-new-project?utm_source=Binary%3A+Launchpad&utm_medium=Debug+Tab&utm_campaign=Learn+More') - cy.percySnapshot() - - cy.viewport(700, 700) - - cy.percySnapshot('responsive') - }) - - it('sends record event upon seeing slideshow', () => { - useUserProjectStatusStore().setUserFlag('isLoggedIn', false) - mountWithGql(, { debugSlideshowComplete: false }) - cy.get('@recordEvent').should('have.been.calledWithMatch', { campaign: DEBUG_SLIDESHOW.campaigns.connectProject, messageId: Cypress.sinon.match.string, medium: DEBUG_SLIDESHOW.medium, cohort: Cypress.sinon.match(/A|B/) }) }) }) @@ -125,16 +105,10 @@ describe('Debug page empty states', () => { it('renders', () => { mountWithGql() - cy.findByRole('link', { name: 'Learn more about recording a run to Cypress Cloud' }).should('have.attr', 'href', 'https://on.cypress.io/cypress-run-record-key?utm_source=Binary%3A+Launchpad&utm_medium=Debug+Tab&utm_campaign=Learn+More') + cy.findByText('Copy the command below to record your first run').should('be.visible') cy.percySnapshot() }) - - it('sends record event upon seeing slideshow', () => { - useUserProjectStatusStore().setUserFlag('isLoggedIn', false) - mountWithGql(, { debugSlideshowComplete: false }) - cy.get('@recordEvent').should('have.been.calledWithMatch', { campaign: DEBUG_SLIDESHOW.campaigns.recordRun, messageId: Cypress.sinon.match.string, medium: DEBUG_SLIDESHOW.medium, cohort: Cypress.sinon.match(/A|B/) }) - }) }) context('loading', () => { @@ -153,57 +127,5 @@ describe('Debug page empty states', () => { cy.percySnapshot() }) - - it('does not render slideshow on error page', () => { - mountWithGql(, { debugSlideshowComplete: false }) - cy.get('@recordEvent').should('not.have.been.called') - cy.findByTestId('debug-default-empty-state').should('be.visible') - cy.findByTestId('debug-slideshow-slide').should('not.exist') - }) - }) - - context('slideshow', () => { - function moveThroughSlideshow (options: { cohort: 'A' | 'B', percy?: boolean }) { - for (const step of [1, 2, 3]) { - const { title, description } = cy.i18n.debugPage.emptyStates.slideshow[`step${step}`] - const imageSrc = `debug-guide-${options.cohort === 'A' ? 'skeleton' : 'text'}-${step}.png` - - cy.findByAltText('Debug tutorial').should('have.attr', 'src').should('include', imageSrc) - cy.contains('h2', title) - cy.contains('p', description) - cy.findByTestId('debug-slideshow-step').contains(`${step}/3`) - cy.contains('button', 'Previous').should(step === 1 ? 'not.exist' : 'be.visible') - if (options.percy) { - cy.percySnapshot(`slideshow step ${step}`) - } - - cy.contains('button', step === 3 ? 'Done' : 'Next').click() - } - - cy.findByTestId('debug-slideshow-slide').should('not.exist') - } - - it('renders slideshow if debugSlideshowComplete = false', () => { - useUserProjectStatusStore().setUserFlag('isLoggedIn', false) - mountWithGql(, { cohort: 'B', debugSlideshowComplete: false }) - cy.get('@recordEvent').should('have.been.calledWithMatch', { campaign: DEBUG_SLIDESHOW.campaigns.recordRun, messageId: Cypress.sinon.match.string, medium: DEBUG_SLIDESHOW.medium, cohort: Cypress.sinon.match(/A|B/) }) - moveThroughSlideshow({ cohort: 'B', percy: true }) - cy.get('@storeSlideshowComplete').should('have.been.called') - - // Can still move through slideshow, does not record events after completion - cy.contains('button', 'Info').click() - cy.get('@recordEvent').should('not.have.been.calledTwice') - moveThroughSlideshow({ cohort: 'B' }) - cy.get('@storeSlideshowComplete').should('not.have.been.calledTwice') - }) - - it('renders default empty state if debugSlideshowComplete = true', () => { - useUserProjectStatusStore().setUserFlag('isLoggedIn', false) - mountWithGql(, { cohort: 'A', debugSlideshowComplete: true }) - cy.findByTestId('debug-default-empty-state') - - cy.contains('button', 'Info').click() - moveThroughSlideshow({ cohort: 'A', percy: true }) - }) }) }) diff --git a/packages/app/src/debug/empty/DebugEmptyView.vue b/packages/app/src/debug/empty/DebugEmptyView.vue index c4ff0601877d..db4624830c27 100644 --- a/packages/app/src/debug/empty/DebugEmptyView.vue +++ b/packages/app/src/debug/empty/DebugEmptyView.vue @@ -25,87 +25,30 @@ - + + diff --git a/packages/app/src/debug/guide/DebugGuide.vue b/packages/app/src/debug/guide/DebugGuide.vue new file mode 100644 index 000000000000..22bc7073743c --- /dev/null +++ b/packages/app/src/debug/guide/DebugGuide.vue @@ -0,0 +1,52 @@ + + + diff --git a/packages/app/src/debug/guide/DebugTour.vue b/packages/app/src/debug/guide/DebugTour.vue new file mode 100644 index 000000000000..3693f36d4963 --- /dev/null +++ b/packages/app/src/debug/guide/DebugTour.vue @@ -0,0 +1,76 @@ + + + diff --git a/packages/app/src/debug/guide/GuideCard1.vue b/packages/app/src/debug/guide/GuideCard1.vue new file mode 100644 index 000000000000..482320ba7022 --- /dev/null +++ b/packages/app/src/debug/guide/GuideCard1.vue @@ -0,0 +1,35 @@ + + + diff --git a/packages/app/src/debug/guide/GuideCard2.vue b/packages/app/src/debug/guide/GuideCard2.vue new file mode 100644 index 000000000000..0bd1610314af --- /dev/null +++ b/packages/app/src/debug/guide/GuideCard2.vue @@ -0,0 +1,35 @@ + + + diff --git a/packages/app/src/debug/guide/GuideCard3.vue b/packages/app/src/debug/guide/GuideCard3.vue new file mode 100644 index 000000000000..b0580d4ee995 --- /dev/null +++ b/packages/app/src/debug/guide/GuideCard3.vue @@ -0,0 +1,34 @@ + + + diff --git a/packages/app/src/debug/guide/TourCard.vue b/packages/app/src/debug/guide/TourCard.vue new file mode 100644 index 000000000000..1b464318ef7e --- /dev/null +++ b/packages/app/src/debug/guide/TourCard.vue @@ -0,0 +1,36 @@ + + + diff --git a/packages/app/src/debug/utils/constants.ts b/packages/app/src/debug/utils/constants.ts index eeeb1cb4a920..a82ed2833664 100644 --- a/packages/app/src/debug/utils/constants.ts +++ b/packages/app/src/debug/utils/constants.ts @@ -1,15 +1,7 @@ -type ValueOf = T[keyof T] - export const DEBUG_TAB_MEDIUM = 'Debug Tab' -export const DEBUG_SLIDESHOW = { - id: 'iatr_debug_slideshow', - campaigns: { - login: 'Debug Login Empty State', - connectProject: 'Debug Connect Project Empty State', - recordRun: 'Debug Record Run Empty State', - }, - medium: DEBUG_TAB_MEDIUM, +export const DEBUG_PROMO_CAMPAIGNS = { + login: 'Debug Login Empty State', + connectProject: 'Debug Connect Project Empty State', + recordRun: 'Debug Record Run Empty State', } as const - -export type DebugSlideshowCampaigns = ValueOf diff --git a/packages/app/src/runs/RunsConnect.cy.tsx b/packages/app/src/runs/RunsConnect.cy.tsx index 067f9b01af1b..70614009e14e 100644 --- a/packages/app/src/runs/RunsConnect.cy.tsx +++ b/packages/app/src/runs/RunsConnect.cy.tsx @@ -2,7 +2,7 @@ import RunsConnect from './RunsConnect.vue' describe('', () => { it('show connect button', () => { - cy.mount(() =>
) + cy.mount(() =>
) cy.contains('button', 'Connect to Cypress Cloud').should('be.visible') }) diff --git a/packages/app/src/runs/RunsConnect.vue b/packages/app/src/runs/RunsConnect.vue index 41306e153184..d7935d7548cb 100644 --- a/packages/app/src/runs/RunsConnect.vue +++ b/packages/app/src/runs/RunsConnect.vue @@ -1,50 +1,17 @@ diff --git a/packages/app/src/runs/RunsContainer.cy.tsx b/packages/app/src/runs/RunsContainer.cy.tsx index 18e62d7b66c9..358c6051179f 100644 --- a/packages/app/src/runs/RunsContainer.cy.tsx +++ b/packages/app/src/runs/RunsContainer.cy.tsx @@ -56,9 +56,6 @@ describe('', { keystrokeDelay: 0 }, () => { const text = defaultMessages.runs.connect cy.contains(text.title).should('be.visible') - cy.contains(text.smartText).should('be.visible') - cy.contains(text.debugText).should('be.visible') - cy.contains(text.chartText).should('be.visible') cy.contains(text.buttonProject).should('be.visible') cy.percySnapshot() }) @@ -75,14 +72,36 @@ describe('', { keystrokeDelay: 0 }, () => { const text = defaultMessages.runs.connect cy.contains(text.title).should('be.visible') - cy.contains(text.smartText).should('be.visible') - cy.contains(text.debugText).should('be.visible') - cy.contains(text.chartText).should('be.visible') cy.contains(text.buttonUser).should('be.visible') cy.percySnapshot() }) }) + context('when the user has no recorded runs', () => { + it('renders instructions and record prompt', () => { + cy.mountFragment(RunsContainerFragmentDoc, { + onResult (gql) { + gql.cloudViewer = cloudViewer + if (gql.currentProject?.cloudProject?.__typename === 'CloudProject') { + gql.currentProject.cloudProject.runs = { + __typename: 'CloudRunConnection', + pageInfo: null as any, + nodes: [], + } + } + }, + render (gqlVal) { + return + }, + }) + + const text = defaultMessages.runs.empty + + cy.contains(text.title).should('be.visible') + cy.percySnapshot() + }) + }) + context('with errors', () => { it('renders connection failed', () => { cy.mountFragment(RunsContainerFragmentDoc, { diff --git a/packages/app/src/runs/RunsContainer.vue b/packages/app/src/runs/RunsContainer.vue index 92ca151d8029..ede14df2c0df 100644 --- a/packages/app/src/runs/RunsContainer.vue +++ b/packages/app/src/runs/RunsContainer.vue @@ -1,5 +1,5 @@