diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 59fab58cbd..987778181f 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,10 @@ _Released 05/09/2023 (PENDING)_ +**Features:** + +- Added a new informational banner to help get started with component testing from an existing end-to-end test suite. Addresses [#26511](https://github.com/cypress-io/cypress/issues/26511). + **Bugfixes:** - Fixed an issue in Electron where devtools gets out of sync with the DOM occasionally. Addresses [#15932](https://github.com/cypress-io/cypress/issues/15932). diff --git a/packages/app/package.json b/packages/app/package.json index 36cc45a7f4..75dfd7ed73 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -20,8 +20,9 @@ }, "dependencies": {}, "devDependencies": { - "@cypress-design/vue-icon": "0.20.0", - "@cypress-design/vue-statusicon": "0.3.0", + "@cypress-design/vue-button": "0.9.2", + "@cypress-design/vue-icon": "0.22.1", + "@cypress-design/vue-statusicon": "0.4.3", "@graphql-typed-document-node/core": "^3.1.0", "@headlessui/vue": "1.4.0", "@iconify/iconify": "2.1.2", diff --git a/packages/app/src/composables/useRelevantRun.ts b/packages/app/src/composables/useRelevantRun.ts index 25e4027799..549fbf8acd 100644 --- a/packages/app/src/composables/useRelevantRun.ts +++ b/packages/app/src/composables/useRelevantRun.ts @@ -1,6 +1,6 @@ import { gql, useSubscription } from '@urql/vue' import { Debug_RelevantRuns_SubscriptionDocument, Sidebar_RelevantRuns_SubscriptionDocument } from '@packages/app/src/generated/graphql' -import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store' +import { useUserProjectStatusStore } from '@packages/frontend-shared/src/store/user-project-status-store' import { computed } from 'vue' import { uniq } from 'lodash' @@ -43,10 +43,10 @@ gql` ` export function useRelevantRun (location: 'SIDEBAR' | 'DEBUG') { - const loginConnectStore = useLoginConnectStore() + const userProjectStatusStore = useUserProjectStatusStore() const shouldPause = computed(() => { - return !loginConnectStore.project.isProjectConnected + return !userProjectStatusStore.project.isProjectConnected }) //Switch the subscription query depending on where it was registered from diff --git a/packages/app/src/debug/DebugContainer.cy.tsx b/packages/app/src/debug/DebugContainer.cy.tsx index e4002b096b..3b1511300e 100644 --- a/packages/app/src/debug/DebugContainer.cy.tsx +++ b/packages/app/src/debug/DebugContainer.cy.tsx @@ -1,7 +1,7 @@ import { DebugSpecListGroupsFragment, DebugSpecListSpecFragment, DebugSpecListTestsFragment, DebugSpecsFragment, DebugSpecsFragmentDoc, UseCohorts_DetermineCohortDocument } from '../generated/graphql-test' import DebugContainer from './DebugContainer.vue' import { defaultMessages } from '@cy/i18n' -import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store' +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' @@ -51,31 +51,31 @@ describe('', () => { } it('shows not logged in', () => { - const loginConnectStore = useLoginConnectStore() + const userProjectStatusStore = useUserProjectStatusStore() - loginConnectStore.setHasInitiallyLoaded() + userProjectStatusStore.setHasInitiallyLoaded() validateEmptyState([defaultMessages.debugPage.emptyStates.connectToCypressCloud, defaultMessages.debugPage.emptyStates.debugDirectlyInCypress, defaultMessages.debugPage.emptyStates.notLoggedInTestMessage]) cy.findByRole('button', { name: 'Connect to Cypress Cloud' }).should('be.visible') }) it('is logged in with no project', () => { - const loginConnectStore = useLoginConnectStore() + const userProjectStatusStore = useUserProjectStatusStore() - loginConnectStore.setUserFlag('isLoggedIn', true) - loginConnectStore.setProjectFlag('isProjectConnected', false) - loginConnectStore.setHasInitiallyLoaded() + userProjectStatusStore.setUserFlag('isLoggedIn', true) + userProjectStatusStore.setProjectFlag('isProjectConnected', false) + userProjectStatusStore.setHasInitiallyLoaded() validateEmptyState([defaultMessages.debugPage.emptyStates.debugDirectlyInCypress, defaultMessages.debugPage.emptyStates.reviewRerunAndDebug, defaultMessages.debugPage.emptyStates.noProjectTestMessage]) cy.findByRole('button', { name: 'Connect a Cypress Cloud project' }).should('be.visible') }) it('has no runs', () => { - const loginConnectStore = useLoginConnectStore() + const userProjectStatusStore = useUserProjectStatusStore() - loginConnectStore.setUserFlag('isLoggedIn', true) - loginConnectStore.setProjectFlag('isProjectConnected', true) - loginConnectStore.setHasInitiallyLoaded() + userProjectStatusStore.setUserFlag('isLoggedIn', true) + userProjectStatusStore.setProjectFlag('isProjectConnected', true) + userProjectStatusStore.setHasInitiallyLoaded() cy.mountFragment(DebugSpecsFragmentDoc, { variableTypes: DebugSpecVariableTypes, variables: defaultVariables, @@ -87,11 +87,11 @@ describe('', () => { }) it('errors', () => { - const loginConnectStore = useLoginConnectStore() + const userProjectStatusStore = useUserProjectStatusStore() - loginConnectStore.setUserFlag('isLoggedIn', true) - loginConnectStore.setProjectFlag('isProjectConnected', true) - loginConnectStore.setHasInitiallyLoaded() + userProjectStatusStore.setUserFlag('isLoggedIn', true) + userProjectStatusStore.setProjectFlag('isProjectConnected', true) + userProjectStatusStore.setHasInitiallyLoaded() cy.mountFragment(DebugSpecsFragmentDoc, { variableTypes: DebugSpecVariableTypes, variables: defaultVariables, @@ -105,11 +105,11 @@ describe('', () => { describe('run states', { viewportWidth: 900 }, () => { beforeEach(() => { - const loginConnectStore = useLoginConnectStore() + const userProjectStatusStore = useUserProjectStatusStore() - loginConnectStore.setUserFlag('isLoggedIn', true) - loginConnectStore.setProjectFlag('isProjectConnected', true) - loginConnectStore.setHasInitiallyLoaded() + userProjectStatusStore.setUserFlag('isLoggedIn', true) + userProjectStatusStore.setProjectFlag('isProjectConnected', true) + userProjectStatusStore.setHasInitiallyLoaded() }) function mountTestRun (runName: string) { @@ -229,14 +229,14 @@ describe('', () => { }) describe('when logged in and connected', () => { - let loginConnectStore + let userProjectStatusStore beforeEach(() => { - loginConnectStore = useLoginConnectStore() + userProjectStatusStore = useUserProjectStatusStore() - loginConnectStore.setUserFlag('isLoggedIn', true) - loginConnectStore.setProjectFlag('isProjectConnected', true) - loginConnectStore.setHasInitiallyLoaded() + userProjectStatusStore.setUserFlag('isLoggedIn', true) + userProjectStatusStore.setProjectFlag('isProjectConnected', true) + userProjectStatusStore.setHasInitiallyLoaded() }) it('renders running run', () => { diff --git a/packages/app/src/debug/DebugContainer.vue b/packages/app/src/debug/DebugContainer.vue index e0ef009841..7b9e722e9d 100644 --- a/packages/app/src/debug/DebugContainer.vue +++ b/packages/app/src/debug/DebugContainer.vue @@ -4,16 +4,16 @@ {{ t('launchpadErrors.noInternet.connectProject') }} - + { return props.gql?.currentProject?.cloudProject?.__typename === 'CloudProject' diff --git a/packages/app/src/debug/empty/DebugEmptyStates.cy.tsx b/packages/app/src/debug/empty/DebugEmptyStates.cy.tsx index e21d6c7ee9..6dd4c6aa62 100644 --- a/packages/app/src/debug/empty/DebugEmptyStates.cy.tsx +++ b/packages/app/src/debug/empty/DebugEmptyStates.cy.tsx @@ -4,7 +4,7 @@ import DebugNoRuns from './DebugNoRuns.vue' import DebugLoading from './DebugLoading.vue' import DebugError from './DebugError.vue' import DebugEmptyView from './DebugEmptyView.vue' -import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store' +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' @@ -77,10 +77,10 @@ describe('Debug page empty states', () => { context('not logged in', () => { it('renders', () => { - const loginConnectStore = useLoginConnectStore() + const userProjectStatusStore = useUserProjectStatusStore() // We need to set isLoggedIn so that CloudConnectButton shows the correct state - loginConnectStore.setUserFlag('isLoggedIn', false) + userProjectStatusStore.setUserFlag('isLoggedIn', false) mountWithGql() @@ -90,7 +90,7 @@ describe('Debug page empty states', () => { }) it('sends record event upon seeing slideshow', () => { - useLoginConnectStore().setUserFlag('isLoggedIn', false) + 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/) }) }) @@ -98,10 +98,10 @@ describe('Debug page empty states', () => { context('no project', () => { it('renders', () => { - const loginConnectStore = useLoginConnectStore() + const userProjectStatusStore = useUserProjectStatusStore() // We need to set isLoggedIn so that CloudConnectButton shows the correct state - loginConnectStore.setUserFlag('isLoggedIn', true) + userProjectStatusStore.setUserFlag('isLoggedIn', true) mountWithGql() @@ -115,7 +115,7 @@ describe('Debug page empty states', () => { }) it('sends record event upon seeing slideshow', () => { - useLoginConnectStore().setUserFlag('isLoggedIn', false) + 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/) }) }) @@ -131,7 +131,7 @@ describe('Debug page empty states', () => { }) it('sends record event upon seeing slideshow', () => { - useLoginConnectStore().setUserFlag('isLoggedIn', false) + 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/) }) }) @@ -184,7 +184,7 @@ describe('Debug page empty states', () => { } it('renders slideshow if debugSlideshowComplete = false', () => { - useLoginConnectStore().setUserFlag('isLoggedIn', 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 }) @@ -198,7 +198,7 @@ describe('Debug page empty states', () => { }) it('renders default empty state if debugSlideshowComplete = true', () => { - useLoginConnectStore().setUserFlag('isLoggedIn', false) + useUserProjectStatusStore().setUserFlag('isLoggedIn', false) mountWithGql(, { cohort: 'A', debugSlideshowComplete: true }) cy.findByTestId('debug-default-empty-state') diff --git a/packages/app/src/navigation/SidebarNavigation.cy.tsx b/packages/app/src/navigation/SidebarNavigation.cy.tsx index 3da07c8c74..37ceba7806 100644 --- a/packages/app/src/navigation/SidebarNavigation.cy.tsx +++ b/packages/app/src/navigation/SidebarNavigation.cy.tsx @@ -3,7 +3,7 @@ import { defaultMessages } from '@cy/i18n' import { CloudRunStatus, SidebarNavigationFragment, SidebarNavigationFragmentDoc, SideBarNavigation_SetPreferencesDocument } from '../generated/graphql-test' import { CloudRunStubs } from '@packages/graphql/test/stubCloudTypes' import { cloneDeep } from 'lodash' -import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store' +import { useUserProjectStatusStore } from '@packages/frontend-shared/src/store/user-project-status-store' function mountComponent (props: { initialNavExpandedVal?: boolean, cloudProject?: { status: CloudRunStatus, numFailedTests: number }, isLoading?: boolean, online?: boolean} = {}) { const withDefaults = { initialNavExpandedVal: false, isLoading: false, online: true, ...props } @@ -186,9 +186,9 @@ describe('SidebarNavigation', () => { }) it('renders no badge when query is loading', () => { - const loginConnectStore = useLoginConnectStore() + const userProjectStatusStore = useUserProjectStatusStore() - loginConnectStore.setProjectFlag('isProjectConnected', true) + userProjectStatusStore.setProjectFlag('isProjectConnected', true) mountComponent({ isLoading: true }) diff --git a/packages/app/src/navigation/SidebarNavigation.vue b/packages/app/src/navigation/SidebarNavigation.vue index 5d65c657b2..3ca27db5b2 100644 --- a/packages/app/src/navigation/SidebarNavigation.vue +++ b/packages/app/src/navigation/SidebarNavigation.vue @@ -108,7 +108,7 @@ import { useI18n } from '@cy/i18n' import { useRoute } from 'vue-router' import SidebarNavigationHeader from './SidebarNavigationHeader.vue' import { useDebounceFn, useWindowSize } from '@vueuse/core' -import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store' +import { useUserProjectStatusStore } from '@packages/frontend-shared/src/store/user-project-status-store' const { t } = useI18n() @@ -162,7 +162,7 @@ const props = defineProps<{ const NAV_EXPAND_MIN_SCREEN_WIDTH = 1024 -const loginConnectStore = useLoginConnectStore() +const userProjectStatusStore = useUserProjectStatusStore() const debugBadge = ref() @@ -171,7 +171,7 @@ const setDebugBadge = useDebounceFn((badge) => { }, 500) watchEffect(() => { - if (props.isLoading && loginConnectStore.project.isProjectConnected) { + if (props.isLoading && userProjectStatusStore.project.isProjectConnected) { setDebugBadge(undefined) return diff --git a/packages/app/src/runs/CloudConnectButton.cy.tsx b/packages/app/src/runs/CloudConnectButton.cy.tsx index 35e9c5825e..c4361d619a 100644 --- a/packages/app/src/runs/CloudConnectButton.cy.tsx +++ b/packages/app/src/runs/CloudConnectButton.cy.tsx @@ -1,5 +1,5 @@ import CloudConnectButton from './CloudConnectButton.vue' -import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store' +import { useUserProjectStatusStore } from '@packages/frontend-shared/src/store/user-project-status-store' describe('', { viewportHeight: 60, viewportWidth: 400 }, () => { context('not logged in ', () => { @@ -11,12 +11,12 @@ describe('', { viewportHeight: 60, viewportWidth: 400 }, ( }) context('logged in', () => { - let loginConnectStore + let userProjectStatusStore beforeEach(() => { - loginConnectStore = useLoginConnectStore() + userProjectStatusStore = useUserProjectStatusStore() - loginConnectStore.setUserFlag('isLoggedIn', true) + userProjectStatusStore.setUserFlag('isLoggedIn', true) }) it('show project connect if not connected', () => { @@ -26,7 +26,7 @@ describe('', { viewportHeight: 60, viewportWidth: 400 }, ( }) it('uses the store to open the Login Connect modal', () => { - loginConnectStore.openLoginConnectModal = cy.spy().as('openLoginConnectModal') + userProjectStatusStore.openLoginConnectModal = cy.spy().as('openLoginConnectModal') cy.mount(() =>
) cy.contains('button', 'Connect a Cypress Cloud project').click() @@ -35,7 +35,7 @@ describe('', { viewportHeight: 60, viewportWidth: 400 }, ( }) it('uses the store to open the Login Connect modal with utmContent', () => { - loginConnectStore.openLoginConnectModal = cy.spy().as('openLoginConnectModal') + userProjectStatusStore.openLoginConnectModal = cy.spy().as('openLoginConnectModal') cy.mount(() =>
) cy.contains('button', 'Connect a Cypress Cloud project').click() diff --git a/packages/app/src/runs/CloudConnectButton.vue b/packages/app/src/runs/CloudConnectButton.vue index 255d8d3899..ab5a3b25f6 100644 --- a/packages/app/src/runs/CloudConnectButton.vue +++ b/packages/app/src/runs/CloudConnectButton.vue @@ -15,9 +15,9 @@ import ChainIcon from '~icons/cy/chain-link_x16.svg' import CypressIcon from '~icons/cy/cypress-logo_x16.svg' import Button from '@cy/components/Button.vue' import { useI18n } from '@cy/i18n' -import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store' +import { useUserProjectStatusStore } from '@packages/frontend-shared/src/store/user-project-status-store' -const { openLoginConnectModal, user } = useLoginConnectStore() +const { openLoginConnectModal, user } = useUserProjectStatusStore() const { t } = useI18n() diff --git a/packages/app/src/runs/RunsContainer.cy.tsx b/packages/app/src/runs/RunsContainer.cy.tsx index 8dc6c46e1f..18e62d7b66 100644 --- a/packages/app/src/runs/RunsContainer.cy.tsx +++ b/packages/app/src/runs/RunsContainer.cy.tsx @@ -1,7 +1,7 @@ import RunsContainer from './RunsContainer.vue' import { RunsContainerFragmentDoc } from '../generated/graphql-test' import { CloudUserStubs } from '@packages/graphql/test/stubCloudTypes' -import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store' +import { useUserProjectStatusStore } from '@packages/frontend-shared/src/store/user-project-status-store' import { defaultMessages } from '@cy/i18n' @@ -16,9 +16,9 @@ describe('', { keystrokeDelay: 0 }, () => { context('when the user is logged in', () => { beforeEach(() => { - const loginConnectStore = useLoginConnectStore() + const userProjectStatusStore = useUserProjectStatusStore() - loginConnectStore.setUserFlag('isLoggedIn', true) + userProjectStatusStore.setUserFlag('isLoggedIn', true) }) it('renders with expected runs if there is a cloud project id', () => { diff --git a/packages/app/src/runs/RunsContainer.vue b/packages/app/src/runs/RunsContainer.vue index f33bc62609..92ca151d80 100644 --- a/packages/app/src/runs/RunsContainer.vue +++ b/packages/app/src/runs/RunsContainer.vue @@ -52,7 +52,7 @@ import RunsEmpty from './RunsEmpty.vue' import { RunsContainerFragment, RunsContainer_FetchNewerRunsDocument } from '../generated/graphql' import Warning from '@packages/frontend-shared/src/warning/Warning.vue' import RunsErrorRenderer from './RunsErrorRenderer.vue' -import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store' +import { useUserProjectStatusStore } from '@packages/frontend-shared/src/store/user-project-status-store' const { t } = useI18n() @@ -188,9 +188,9 @@ const props = defineProps<{ const showConnectSuccessAlert = ref(false) const connectionFailed = computed(() => !props.gql.currentProject?.cloudProject && props.online) -const loginConnectStore = useLoginConnectStore() +const userProjectStatusStore = useUserProjectStatusStore() -watch(() => loginConnectStore.project.isProjectConnected, (newVal, oldVal) => { +watch(() => userProjectStatusStore.project.isProjectConnected, (newVal, oldVal) => { if (newVal && oldVal === false) { // only show this alert if we have just connected showConnectSuccessAlert.value = true diff --git a/packages/app/src/runs/RunsErrorRenderer.spec.tsx b/packages/app/src/runs/RunsErrorRenderer.spec.tsx index 45a4d88da2..68dc15d9c3 100644 --- a/packages/app/src/runs/RunsErrorRenderer.spec.tsx +++ b/packages/app/src/runs/RunsErrorRenderer.spec.tsx @@ -1,7 +1,7 @@ import { RunsErrorRendererFragmentDoc } from '../generated/graphql-test' import RunsErrorRenderer from './RunsErrorRenderer.vue' import { defaultMessages } from '@cy/i18n' -import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store' +import { useUserProjectStatusStore } from '@packages/frontend-shared/src/store/user-project-status-store' const text = defaultMessages.runs.errors @@ -34,9 +34,9 @@ describe('', () => { }, }) - const loginConnectStore = useLoginConnectStore() + const userProjectStatusStore = useUserProjectStatusStore() - cy.spy(loginConnectStore, 'openLoginConnectModal').as('loginConnectSpy') + cy.spy(userProjectStatusStore, 'openLoginConnectModal').as('loginConnectSpy') cy.contains(text.notFound.title).should('be.visible') cy.contains(text.notFound.description.replace('{0}', 'projectId: "test-project-id"')).should('be.visible') diff --git a/packages/app/src/runs/RunsErrorRenderer.vue b/packages/app/src/runs/RunsErrorRenderer.vue index ba9dfb1496..c4a14962d3 100644 --- a/packages/app/src/runs/RunsErrorRenderer.vue +++ b/packages/app/src/runs/RunsErrorRenderer.vue @@ -71,9 +71,9 @@ import SendIcon from '~icons/cy/paper-airplane_x16.svg' import { useI18n } from '@cy/i18n' import CodeTag from '../../../frontend-shared/src/components/CodeTag.vue' import ExternalLink from '@cy/gql-components/ExternalLink.vue' -import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store' +import { useUserProjectStatusStore } from '@packages/frontend-shared/src/store/user-project-status-store' -const { openLoginConnectModal } = useLoginConnectStore() +const { openLoginConnectModal } = useUserProjectStatusStore() const { t } = useI18n() diff --git a/packages/app/src/specs/SpecHeaderCloudDataTooltip.cy.tsx b/packages/app/src/specs/SpecHeaderCloudDataTooltip.cy.tsx index f0e3151375..ce27ab2b4a 100644 --- a/packages/app/src/specs/SpecHeaderCloudDataTooltip.cy.tsx +++ b/packages/app/src/specs/SpecHeaderCloudDataTooltip.cy.tsx @@ -2,7 +2,7 @@ import { SpecHeaderCloudDataTooltipFragmentDoc } from '../generated/graphql-test import SpecHeaderCloudDataTooltip from './SpecHeaderCloudDataTooltip.vue' import { get, set } from 'lodash' import { defaultMessages } from '@cy/i18n' -import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store' +import { useUserProjectStatusStore } from '@packages/frontend-shared/src/store/user-project-status-store' const tooltipContentSelector = '.v-popper' @@ -18,7 +18,7 @@ describe('', () => { docs: string }, ) { - const loginConnectStore = useLoginConnectStore() + const userProjectStatusStore = useUserProjectStatusStore() cy.mountFragment(SpecHeaderCloudDataTooltipFragmentDoc, { onResult: (result) => { @@ -26,22 +26,22 @@ describe('', () => { switch (status) { case 'LOGGED_OUT': - loginConnectStore.setUserFlag('isLoggedIn', false) + userProjectStatusStore.setUserFlag('isLoggedIn', false) break case 'NOT_CONNECTED': - loginConnectStore.setUserFlag('isLoggedIn', true) - loginConnectStore.setUserFlag('isOrganizationLoaded', true) - loginConnectStore.setUserFlag('isMemberOfOrganization', true) - loginConnectStore.setProjectFlag('isProjectConnected', false) - loginConnectStore.setProjectFlag('isConfigLoaded', true) + userProjectStatusStore.setUserFlag('isLoggedIn', true) + userProjectStatusStore.setUserFlag('isOrganizationLoaded', true) + userProjectStatusStore.setUserFlag('isMemberOfOrganization', true) + userProjectStatusStore.setProjectFlag('isProjectConnected', false) + userProjectStatusStore.setProjectFlag('isConfigLoaded', true) break case 'NOT_FOUND': - loginConnectStore.setUserFlag('isLoggedIn', true) - loginConnectStore.setProjectFlag('isNotFound', true) + userProjectStatusStore.setUserFlag('isLoggedIn', true) + userProjectStatusStore.setProjectFlag('isNotFound', true) break case 'ACCESS_REQUESTED': - loginConnectStore.setUserFlag('isLoggedIn', true) - loginConnectStore.setProjectFlag('isNotAuthorized', true) + userProjectStatusStore.setUserFlag('isLoggedIn', true) + userProjectStatusStore.setProjectFlag('isNotAuthorized', true) set(result, 'currentProject.cloudProject', { __typename: 'CloudProjectUnauthorized', @@ -51,16 +51,16 @@ describe('', () => { break case 'UNAUTHORIZED': - loginConnectStore.setUserFlag('isLoggedIn', true) - loginConnectStore.setProjectFlag('isNotAuthorized', true) + userProjectStatusStore.setUserFlag('isLoggedIn', true) + userProjectStatusStore.setProjectFlag('isNotAuthorized', true) break case 'CONNECTED': default: - loginConnectStore.setUserFlag('isLoggedIn', true) - loginConnectStore.setUserFlag('isOrganizationLoaded', true) - loginConnectStore.setUserFlag('isMemberOfOrganization', true) - loginConnectStore.setProjectFlag('isProjectConnected', true) + userProjectStatusStore.setUserFlag('isLoggedIn', true) + userProjectStatusStore.setUserFlag('isOrganizationLoaded', true) + userProjectStatusStore.setUserFlag('isMemberOfOrganization', true) + userProjectStatusStore.setProjectFlag('isProjectConnected', true) break } }, diff --git a/packages/app/src/specs/SpecHeaderCloudDataTooltip.vue b/packages/app/src/specs/SpecHeaderCloudDataTooltip.vue index 3166d79a33..0e31231318 100644 --- a/packages/app/src/specs/SpecHeaderCloudDataTooltip.vue +++ b/packages/app/src/specs/SpecHeaderCloudDataTooltip.vue @@ -44,7 +44,7 @@
@@ -112,10 +112,13 @@ @@ -135,15 +138,15 @@ import RequestAccessButton from './RequestAccessButton.vue' import { gql } from '@urql/vue' import { SpecsListBannersFragment, SpecsListBanners_CheckCloudOrgMembershipDocument } from '../generated/graphql' import { AllowedState, BannerIds } from '@packages/types' -import { LoginBanner, CreateOrganizationBanner, ConnectProjectBanner, RecordBanner } from './banners' -import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store' +import { LoginBanner, ComponentTestingAvailableBanner, CreateOrganizationBanner, ConnectProjectBanner, RecordBanner } from './banners' +import { useUserProjectStatusStore } from '@packages/frontend-shared/src/store/user-project-status-store' import { usePromptManager } from '@packages/frontend-shared/src/gql-components/composables/usePromptManager' import { CohortConfig, CohortOption, useCohorts } from '@packages/frontend-shared/src/gql-components/composables/useCohorts' import { useSubscription } from '../graphql' const route = useRoute() const { t } = useI18n() -const loginConnectStore = useLoginConnectStore() +const userProjectStatusStore = useUserProjectStatusStore() gql` fragment SpecsListBanners on Query { @@ -163,6 +166,21 @@ fragment SpecsListBanners on Query { id projectId savedState + currentTestingType + config + } + machineId + wizard { + framework { + id + name + icon + type + } + bundler { + id + name + } } } ` @@ -203,13 +221,12 @@ const showFetchError = ref(props.isFetchError) const showProjectNotFound = ref(props.isProjectNotFound) const showProjectRequestAccess = ref(props.isProjectUnauthorized) -const isBannerAllowed = ref(false) - const bannerIds = { isLoggedOut: BannerIds.ACI_082022_LOGIN, needsOrgConnect: BannerIds.ACI_082022_CREATE_ORG, needsProjectConnect: BannerIds.ACI_082022_CONNECT_PROJECT, needsRecordedRun: BannerIds.ACI_082022_RECORD, + isComponentTestingCandidate: BannerIds.CT_052023_AVAILABLE, } as const watch( @@ -223,32 +240,30 @@ watch( }, ) -const cloudData = computed(() => ([props.gql.cloudViewer, props.gql.cachedUser, props.gql.currentProject] as const)) -const bannerToShow = computed(() => { +const { getEffectiveBannerState } = usePromptManager() + +const bannerComponentToShow = computed(() => { const componentsByStatus = { isLoggedOut: LoginBanner, needsOrgConnect: CreateOrganizationBanner, needsProjectConnect: ConnectProjectBanner, needsRecordedRun: RecordBanner, + isComponentTestingCandidate: ComponentTestingAvailableBanner, } - return componentsByStatus[loginConnectStore.userStatus] ?? null + const bannerStateToShow = getEffectiveBannerState('specsListBanner') + + return bannerStateToShow ? componentsByStatus[bannerStateToShow] : null }) const hasCurrentBannerBeenShown = computed(() => { + const bannerStateToShow = getEffectiveBannerState('specsListBanner') const bannersState = (props.gql.currentProject?.savedState as AllowedState)?.banners + const bannerId = bannerStateToShow && bannerIds[bannerStateToShow] - return !!bannersState?._disabled || !!bannersState?.[bannerIds[loginConnectStore.userStatus]]?.lastShown + return !!bannersState?._disabled || (!!bannerId && !!bannersState?.[bannerId]?.lastShown) }) -const { isAllowedFeature } = usePromptManager() - -watch(cloudData, () => { - // when cloud data updates, recheck if banner is allowed - isBannerAllowed.value = isAllowedFeature('specsListBanner') -}, -{ immediate: true }) - type BannerKeys = keyof typeof BannerIds type BannerId = typeof BannerIds[BannerKeys] type BannerCohortOptions = Partial> @@ -280,11 +295,21 @@ const getCohortForBanner = (bannerId: BannerId): Ref = } const currentCohortOption = computed(() => { - if (!bannerCohortOptions[bannerIds[loginConnectStore.userStatus]]) { + if (!bannerCohortOptions[bannerIds[userProjectStatusStore.cloudStatus]]) { return { cohort: null } } - return reactive({ cohort: getCohortForBanner(bannerIds[loginConnectStore.userStatus]) }) + return reactive({ cohort: getCohortForBanner(bannerIds[userProjectStatusStore.cloudStatus]) }) }) +const ctFramework = computed(() => { + return { + name: props.gql.wizard?.framework?.name, + type: props.gql.wizard?.framework?.type, + icon: props.gql.wizard?.framework?.icon, + } +}) + +const ctBundler = computed(() => props.gql.wizard?.bundler?.name) + diff --git a/packages/app/src/specs/banners/ComponentTestingAvailableBanner.cy.tsx b/packages/app/src/specs/banners/ComponentTestingAvailableBanner.cy.tsx new file mode 100644 index 0000000000..64253ee0a9 --- /dev/null +++ b/packages/app/src/specs/banners/ComponentTestingAvailableBanner.cy.tsx @@ -0,0 +1,87 @@ +import { defaultMessages } from '@cy/i18n' +import ComponentTestingAvailableBanner from './ComponentTestingAvailableBanner.vue' +import { TrackedBanner_RecordBannerSeenDocument, TrackedBanner_SetProjectStateDocument } from '../../generated/graphql' +import type Sinon from 'sinon' + +const frameworks = [ + { name: 'React', type: 'react' }, + { name: 'Create React App', type: 'reactscripts' }, + { name: 'Nuxt.js (v2)', type: 'nuxtjs' }, + { name: 'Vue', type: 'vue3' }, + { name: 'Angular', type: 'angular' }, + { name: 'Next.js', type: 'nextjs' }, + { name: 'Svelte.js', type: 'svelte' }, +] + +describe('', { viewportWidth: 1200 }, () => { + it('should render expected content', () => { + cy.mount() + }) + + frameworks.map((framework) => { + it(`should render expected content for ${framework.name}`, () => { + cy.mount( + , + ) + + cy.findByTestId('alert-prefix-icon').should('be.visible') + cy.contains(defaultMessages.specPage.banners.ct.title.replace('{0}', framework.name)).should('be.visible') + }) + }) + + context('events', () => { + beforeEach(() => { + const recordEvent = cy.stub().as('recordEvent') + const setPrefs = cy.stub().as('setPrefs') + + cy.stubMutationResolver(TrackedBanner_RecordBannerSeenDocument, (defineResult, event) => { + recordEvent(event) + + return defineResult({ recordEvent: true }) + }) + + cy.stubMutationResolver(TrackedBanner_SetProjectStateDocument, (defineResult, event) => { + setPrefs(event) + + return defineResult({ __typename: 'Mutation', setPreferences: { __typename: 'Query' } as any }) + }) + }) + + it('should record expected event on mount', () => { + cy.mount( + , + ) + + cy.get('@recordEvent').should('have.been.calledWith', { + campaign: 'CT Available', + medium: 'Specs CT Available Banner', + messageId: Cypress.sinon.match.string, + cohort: null, + }) + }) + + it('should not record event on mount if already shown', () => { + cy.mount( + , + ) + + cy.get('@recordEvent').should('not.have.been.called') + }) + + it('should record dismissal event when clicking survey link', () => { + cy.mount( + , + ) + + cy.findByTestId('survey-link').click() + + cy.get('@setPrefs').should('have.been.calledTwice') + cy.get('@setPrefs').should(($stub) => { + const arg = ($stub as unknown as Sinon.SinonStub).getCall(1).args[0] + + expect(arg.value).to.contain('ct_052023_available') + expect(arg.value).to.contain('dismissed') + }) + }) + }) +}) diff --git a/packages/app/src/specs/banners/ComponentTestingAvailableBanner.vue b/packages/app/src/specs/banners/ComponentTestingAvailableBanner.vue new file mode 100644 index 0000000000..b3b75b72d7 --- /dev/null +++ b/packages/app/src/specs/banners/ComponentTestingAvailableBanner.vue @@ -0,0 +1,145 @@ + + + diff --git a/packages/app/src/specs/banners/ConnectProjectBanner.vue b/packages/app/src/specs/banners/ConnectProjectBanner.vue index ffa6377d6f..ed8fc0861e 100644 --- a/packages/app/src/specs/banners/ConnectProjectBanner.vue +++ b/packages/app/src/specs/banners/ConnectProjectBanner.vue @@ -37,8 +37,8 @@ import Button from '@cy/components/Button.vue' import TrackedBanner from './TrackedBanner.vue' import type { CohortOption } from '@packages/frontend-shared/src/gql-components/composables/useCohorts' import { BannerIds } from '@packages/types' -import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store' -const { openLoginConnectModal } = useLoginConnectStore() +import { useUserProjectStatusStore } from '@packages/frontend-shared/src/store/user-project-status-store' +const { openLoginConnectModal } = useUserProjectStatusStore() defineProps<{ hasBannerBeenShown: boolean diff --git a/packages/app/src/specs/banners/LoginBanner.vue b/packages/app/src/specs/banners/LoginBanner.vue index acb6faa1d8..5d0e0853a0 100644 --- a/packages/app/src/specs/banners/LoginBanner.vue +++ b/packages/app/src/specs/banners/LoginBanner.vue @@ -37,8 +37,8 @@ import Button from '@cy/components/Button.vue' import TrackedBanner from './TrackedBanner.vue' import type { CohortOption } from '@packages/frontend-shared/src/gql-components/composables/useCohorts' import { BannerIds } from '@packages/types' -import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store' -const { openLoginConnectModal } = useLoginConnectStore() +import { useUserProjectStatusStore } from '@packages/frontend-shared/src/store/user-project-status-store' +const { openLoginConnectModal } = useUserProjectStatusStore() defineProps<{ hasBannerBeenShown: boolean diff --git a/packages/app/src/specs/banners/TrackedBanner.vue b/packages/app/src/specs/banners/TrackedBanner.vue index c5bee7a956..7a0d2d8db7 100644 --- a/packages/app/src/specs/banners/TrackedBanner.vue +++ b/packages/app/src/specs/banners/TrackedBanner.vue @@ -3,7 +3,10 @@ v-model="isAlertDisplayed" v-bind="$attrs" > - + @@ -71,22 +74,22 @@ watchEffect(() => { } }) -watch(() => isAlertDisplayed.value, (newVal) => { +watch(() => isAlertDisplayed.value, async (newVal) => { if (!newVal) { - updateBannerState('dismissed') + await updateBannerState('dismissed') } }) -onMounted(() => { - updateBannerState('lastShown') +onMounted(async () => { + await updateBannerState('lastShown') }) -function updateBannerState (field: 'lastShown' | 'dismissed') { +async function updateBannerState (field: 'lastShown' | 'dismissed') { const savedBannerState = stateQuery.data.value?.currentProject?.savedState?.banners ?? {} set(savedBannerState, [props.bannerId, field], Date.now()) - setStateMutation.executeMutation({ value: JSON.stringify({ banners: savedBannerState }) }) + await setStateMutation.executeMutation({ value: JSON.stringify({ banners: savedBannerState }) }) } function recordBannerShown ({ campaign, medium, cohort }: EventData): void { @@ -98,4 +101,8 @@ function recordBannerShown ({ campaign, medium, cohort }: EventData): void { }) } +async function dismiss (): Promise { + await updateBannerState('dismissed') +} + diff --git a/packages/app/src/specs/banners/index.ts b/packages/app/src/specs/banners/index.ts index c952e10fdd..1414b9c752 100644 --- a/packages/app/src/specs/banners/index.ts +++ b/packages/app/src/specs/banners/index.ts @@ -5,3 +5,5 @@ export { default as CreateOrganizationBanner } from './CreateOrganizationBanner. export { default as ConnectProjectBanner } from './ConnectProjectBanner.vue' export { default as RecordBanner } from './RecordBanner.vue' + +export { default as ComponentTestingAvailableBanner } from './ComponentTestingAvailableBanner.vue' diff --git a/packages/app/tailwind.config.cjs b/packages/app/tailwind.config.cjs index de778a97ae..8e706b94f7 100644 --- a/packages/app/tailwind.config.cjs +++ b/packages/app/tailwind.config.cjs @@ -1,8 +1,5 @@ const config = require('@packages/frontend-shared/tailwind.config.cjs') -config.content.files = [ - './src/**/*.{vue,js,ts,jsx,tsx,scss,css}', // - '../frontend-shared/src/**/*.{vue,js,ts,jsx,tsx,scss,css}', -], +config.content.files.push('../frontend-shared/src/**/*.{vue,js,ts,jsx,tsx,scss,css}') module.exports = config diff --git a/packages/data-context/src/actions/EventCollectorActions.ts b/packages/data-context/src/actions/EventCollectorActions.ts index 5d9e0e01e0..9f1af8f461 100644 --- a/packages/data-context/src/actions/EventCollectorActions.ts +++ b/packages/data-context/src/actions/EventCollectorActions.ts @@ -5,11 +5,13 @@ const pkg = require('@packages/root') const debug = Debug('cypress:data-context:actions:EventCollectorActions') -interface CollectableEvent { +interface CollectibleEvent { campaign: string messageId: string medium: string cohort?: string + payload?: object + machineId?: string } /** @@ -23,23 +25,29 @@ export class EventCollectorActions { debug('Using %s environment for Event Collection', cloudEnv) } - async recordEvent (event: CollectableEvent): Promise { + async recordEvent (event: CollectibleEvent, includeMachineId: boolean): Promise { try { const cloudUrl = this.ctx.cloud.getCloudUrl(cloudEnv) + const eventUrl = includeMachineId ? `${cloudUrl}/machine-collect` : `${cloudUrl}/anon-collect` + const headers = { + 'Content-Type': 'application/json', + 'x-cypress-version': pkg.version, + } + + if (includeMachineId) { + event.machineId = (await this.ctx.coreData.machineId) || undefined + } await this.ctx.util.fetch( - `${cloudUrl}/anon-collect`, + eventUrl, { method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-cypress-version': pkg.version, - }, + headers, body: JSON.stringify(event), }, ) - debug(`Recorded event: %o`, event) + debug(`Recorded %s event: %o`, includeMachineId ? 'machine-linked' : 'anonymous', event) return true } catch (err) { diff --git a/packages/data-context/src/actions/WizardActions.ts b/packages/data-context/src/actions/WizardActions.ts index 7a3c4aafed..9aa87d96f3 100644 --- a/packages/data-context/src/actions/WizardActions.ts +++ b/packages/data-context/src/actions/WizardActions.ts @@ -100,6 +100,14 @@ export class WizardActions { this.resetWizard() + await this.initializeFramework() + } + + async initializeFramework () { + if (!this.ctx.currentProject) { + return + } + const detected = await detectFramework(this.ctx.currentProject, this.ctx.coreData.wizard.frameworks) debug('detected %o', detected) diff --git a/packages/data-context/src/data/coreDataShape.ts b/packages/data-context/src/data/coreDataShape.ts index 1dd5322525..529311c62f 100644 --- a/packages/data-context/src/data/coreDataShape.ts +++ b/packages/data-context/src/data/coreDataShape.ts @@ -7,6 +7,7 @@ import type { SocketIONamespace, SocketIOServer } from '@packages/socket' import type { Server } from 'http' import type { ErrorWrapperSource } from '@packages/errors' import type { GitDataSource, LegacyCypressConfigJson } from '../sources' +import { machineId as getMachineId } from 'node-machine-id' export type Maybe = T | null | undefined @@ -123,6 +124,7 @@ export interface CoreDataShape { cliBrowser: string | null cliTestingType: string | null activeBrowser: FoundBrowser | null + machineId: Promise machineBrowsers: Promise | null allBrowsers: Promise | null servers: { @@ -166,6 +168,7 @@ export function makeCoreData (modeOptions: Partial = {}): CoreDa servers: {}, cliBrowser: modeOptions.browser ?? null, cliTestingType: modeOptions.testingType ?? null, + machineId: machineId(), machineBrowsers: null, allBrowsers: null, hasInitializedMode: null, @@ -233,4 +236,12 @@ export function makeCoreData (modeOptions: Partial = {}): CoreDa testsForRunResults: {}, }, } + + async function machineId (): Promise { + try { + return await getMachineId() + } catch (error) { + return null + } + } } diff --git a/packages/data-context/src/sources/VersionsDataSource.ts b/packages/data-context/src/sources/VersionsDataSource.ts index f2bfcf29d1..15b0c301aa 100644 --- a/packages/data-context/src/sources/VersionsDataSource.ts +++ b/packages/data-context/src/sources/VersionsDataSource.ts @@ -9,7 +9,6 @@ import semver from 'semver' const debug = Debug('cypress:data-context:sources:VersionsDataSource') const pkg = require('@packages/root') -const nmi = require('node-machine-id') interface Version { id: string @@ -131,7 +130,7 @@ export class VersionsDataSource { return pkg.version } - const id = await VersionsDataSource.machineId() + const id = (await this.ctx.coreData.machineId) || undefined const manifestHeaders: HeadersInit = { 'Content-Type': 'application/json', @@ -226,14 +225,6 @@ export class VersionsDataSource { } } - private static async machineId (): Promise { - try { - return await nmi.machineId() - } catch (error) { - return undefined - } - } - private isFulfilled (item: PromiseSettledResult): item is PromiseFulfilledResult { return item.status === 'fulfilled' } diff --git a/packages/data-context/test/unit/actions/EventCollectorActions.spec.ts b/packages/data-context/test/unit/actions/EventCollectorActions.spec.ts index e5fe67b195..0e989ebdcb 100644 --- a/packages/data-context/test/unit/actions/EventCollectorActions.spec.ts +++ b/packages/data-context/test/unit/actions/EventCollectorActions.spec.ts @@ -24,13 +24,13 @@ describe('EventCollectorActions', () => { }) context('.recordEvent', () => { - it('makes expected request', async () => { + it('makes expected request for anonymous event', async () => { await actions.recordEvent({ campaign: 'abc', medium: 'def', messageId: 'ghi', cohort: '123', - }) + }, false) expect(ctx.util.fetch).to.have.been.calledOnceWith( sinon.match(/anon-collect$/), // Verify URL ends with expected 'anon-collect' path @@ -38,10 +38,26 @@ describe('EventCollectorActions', () => { ) }) + it('makes expected request for machine-linked event', async () => { + ctx.coreData.machineId = Promise.resolve('xyz') + + await actions.recordEvent({ + campaign: 'abc', + medium: 'def', + messageId: 'ghi', + cohort: '123', + }, true) + + expect(ctx.util.fetch).to.have.been.calledOnceWith( + sinon.match(/machine-collect$/), // Verify URL ends with expected 'machine-collect' path + { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-cypress-version': pkg.version }, body: '{"campaign":"abc","medium":"def","messageId":"ghi","cohort":"123","machineId":"xyz"}' }, + ) + }) + it('resolve true if request succeeds', async () => { (ctx.util.fetch as SinonStub).resolves({} as any) - const result = await actions.recordEvent({ campaign: '', medium: '', messageId: '', cohort: '' }) + const result = await actions.recordEvent({ campaign: '', medium: '', messageId: '', cohort: '' }, false) expect(result).to.eql(true) }) @@ -49,7 +65,7 @@ describe('EventCollectorActions', () => { it('resolves false if request fails', async () => { (ctx.util.fetch as SinonStub).rejects({} as any) - const result = await actions.recordEvent({ campaign: '', medium: '', messageId: '', cohort: '' }) + const result = await actions.recordEvent({ campaign: '', medium: '', messageId: '', cohort: '' }, false) expect(result).to.eql(false) }) diff --git a/packages/data-context/test/unit/sources/VersionsDataSource.spec.ts b/packages/data-context/test/unit/sources/VersionsDataSource.spec.ts index 6828555450..31ae8f2283 100644 --- a/packages/data-context/test/unit/sources/VersionsDataSource.spec.ts +++ b/packages/data-context/test/unit/sources/VersionsDataSource.spec.ts @@ -9,12 +9,10 @@ import { createTestDataContext } from '../helper' import { CYPRESS_REMOTE_MANIFEST_URL, NPM_CYPRESS_REGISTRY_URL } from '@packages/types' const pkg = require('@packages/root') -const nmi = require('node-machine-id') describe('VersionsDataSource', () => { context('.versions', () => { let ctx: DataContext - let nmiStub: sinon.SinonStub let fetchStub: sinon.SinonStub let isDependencyInstalledStub: sinon.SinonStub let mockNow: Date = new Date() @@ -33,6 +31,7 @@ describe('VersionsDataSource', () => { }, } + ctx.coreData.machineId = Promise.resolve('abcd123') ctx.coreData.currentProject = '/abc' ctx.coreData.currentTestingType = 'e2e' @@ -41,7 +40,6 @@ describe('VersionsDataSource', () => { }) beforeEach(() => { - nmiStub = sinon.stub(nmi, 'machineId') sinon.stub(ctx.util, 'fetch').callsFake(fetchStub) sinon.stub(ctx.util, 'isDependencyInstalled').callsFake(isDependencyInstalledStub) sinon.stub(os, 'platform').returns('darwin') @@ -54,8 +52,6 @@ describe('VersionsDataSource', () => { }) it('loads the manifest for the latest version with all headers and queries npm for release dates', async () => { - nmiStub.resolves('abcd123') - fetchStub .withArgs(CYPRESS_REMOTE_MANIFEST_URL, { headers: sinon.match({ @@ -107,7 +103,7 @@ describe('VersionsDataSource', () => { it('resets telemetry data triggering a new call to get the latest version', async () => { const currentCypressVersion = pkg.version - nmiStub.rejects('Error while obtaining machine id') + ctx.coreData.machineId = Promise.resolve(null) ctx.coreData.currentTestingType = 'component' fetchStub @@ -140,8 +136,6 @@ describe('VersionsDataSource', () => { }) it('handles errors fetching version data', async () => { - nmiStub.resolves('abcd123') - fetchStub .withArgs(CYPRESS_REMOTE_MANIFEST_URL, { headers: sinon.match({ @@ -167,8 +161,6 @@ describe('VersionsDataSource', () => { }) it('handles invalid response errors', async () => { - nmiStub.resolves('abcd123') - fetchStub .withArgs(CYPRESS_REMOTE_MANIFEST_URL, { headers: sinon.match({ diff --git a/packages/frontend-shared/package.json b/packages/frontend-shared/package.json index d873f7ce25..07cc6dd6af 100644 --- a/packages/frontend-shared/package.json +++ b/packages/frontend-shared/package.json @@ -19,7 +19,7 @@ "dependencies": {}, "devDependencies": { "@antfu/utils": "^0.3.0", - "@cypress-design/css": "^0.11.0", + "@cypress-design/css": "^0.13.1", "@graphql-typed-document-node/core": "^3.1.0", "@headlessui/vue": "1.4.0", "@iconify/json": "1.1.368", diff --git a/packages/launchpad/src/images/logos/angular.svg b/packages/frontend-shared/src/assets/logos/angular.svg similarity index 100% rename from packages/launchpad/src/images/logos/angular.svg rename to packages/frontend-shared/src/assets/logos/angular.svg diff --git a/packages/launchpad/src/images/logos/nextjs.svg b/packages/frontend-shared/src/assets/logos/nextjs.svg similarity index 100% rename from packages/launchpad/src/images/logos/nextjs.svg rename to packages/frontend-shared/src/assets/logos/nextjs.svg diff --git a/packages/launchpad/src/images/logos/nuxt.svg b/packages/frontend-shared/src/assets/logos/nuxt.svg similarity index 100% rename from packages/launchpad/src/images/logos/nuxt.svg rename to packages/frontend-shared/src/assets/logos/nuxt.svg diff --git a/packages/launchpad/src/images/logos/react.svg b/packages/frontend-shared/src/assets/logos/react.svg similarity index 100% rename from packages/launchpad/src/images/logos/react.svg rename to packages/frontend-shared/src/assets/logos/react.svg diff --git a/packages/launchpad/src/images/logos/svelte.svg b/packages/frontend-shared/src/assets/logos/svelte.svg similarity index 100% rename from packages/launchpad/src/images/logos/svelte.svg rename to packages/frontend-shared/src/assets/logos/svelte.svg diff --git a/packages/launchpad/src/images/logos/vite.svg b/packages/frontend-shared/src/assets/logos/vite.svg similarity index 100% rename from packages/launchpad/src/images/logos/vite.svg rename to packages/frontend-shared/src/assets/logos/vite.svg diff --git a/packages/launchpad/src/images/logos/vue.svg b/packages/frontend-shared/src/assets/logos/vue.svg similarity index 100% rename from packages/launchpad/src/images/logos/vue.svg rename to packages/frontend-shared/src/assets/logos/vue.svg diff --git a/packages/launchpad/src/images/logos/webpack.svg b/packages/frontend-shared/src/assets/logos/webpack.svg similarity index 100% rename from packages/launchpad/src/images/logos/webpack.svg rename to packages/frontend-shared/src/assets/logos/webpack.svg diff --git a/packages/frontend-shared/src/components/Alert.vue b/packages/frontend-shared/src/components/Alert.vue index 9bd1e6fe8a..75f678035c 100644 --- a/packages/frontend-shared/src/components/Alert.vue +++ b/packages/frontend-shared/src/components/Alert.vue @@ -23,7 +23,7 @@ :title="title" :header-class="`${props.headerClass} ${canCollapse ? 'group-hocus:underline' : ''}`" :prefix-icon="prefix?.icon" - :prefix-icon-class="open ? prefix?.classes + ' rotate-180' : prefix?.classes" + :prefix-icon-class="(open && collapsible) ? prefix?.classes + ' rotate-180' : prefix?.classes" :suffix-icon-aria-label="props.dismissible ? t('components.alert.dismissAriaLabel') : ''" :suffix-icon="props.dismissible ? DeleteIcon : null" :suffix-button-class="classes.suffixButtonClass" @@ -73,7 +73,7 @@ diff --git a/packages/frontend-shared/src/gql-components/HeaderBarContent.cy.tsx b/packages/frontend-shared/src/gql-components/HeaderBarContent.cy.tsx index b4e46bea09..5f5d769fc9 100644 --- a/packages/frontend-shared/src/gql-components/HeaderBarContent.cy.tsx +++ b/packages/frontend-shared/src/gql-components/HeaderBarContent.cy.tsx @@ -2,7 +2,7 @@ import { HeaderBar_HeaderBarContentFragmentDoc } from '../generated/graphql-test import HeaderBarContent from './HeaderBarContent.vue' import { defaultMessages } from '@cy/i18n' import { CloudUserStubs } from '@packages/graphql/test/stubCloudTypes' -import { useLoginConnectStore } from '../store/login-connect-store' +import { useUserProjectStatusStore } from '../store/user-project-status-store' const text = defaultMessages.topNav @@ -297,9 +297,9 @@ describe('', { viewportWidth: 1000, viewportHeight: 750 }, ( }) it('the logged in state is correctly presented in header', () => { - const loginConnectStore = useLoginConnectStore() + const userProjectStatusStore = useUserProjectStatusStore() - loginConnectStore.setUserFlag('isLoggedIn', true) + userProjectStatusStore.setUserFlag('isLoggedIn', true) const cloudViewer = { ...CloudUserStubs.me, @@ -313,7 +313,7 @@ describe('', { viewportWidth: 1000, viewportHeight: 750 }, ( fullName: 'Tester Test', } - loginConnectStore.setUserData(cloudViewer) + userProjectStatusStore.setUserData(cloudViewer) cy.mountFragment(HeaderBar_HeaderBarContentFragmentDoc, { onResult: (result) => { diff --git a/packages/frontend-shared/src/gql-components/HeaderBarContent.vue b/packages/frontend-shared/src/gql-components/HeaderBarContent.vue index a9f91d5b43..90532a9fdd 100644 --- a/packages/frontend-shared/src/gql-components/HeaderBarContent.vue +++ b/packages/frontend-shared/src/gql-components/HeaderBarContent.vue @@ -94,18 +94,18 @@ @clear-force-open="isForceOpenAllowed = false" > -
+