Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: IATR empty states #25219

Merged
merged 19 commits into from
Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/app/cypress/e2e/runs.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ describe('App: Runs', { viewportWidth: 1200 }, () => {
name: 'Test User A',
} })

cy.contains('button', 'Log in to Cypress Cloud').click()
cy.contains('button', 'Connect to Cypress Cloud').click()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re: updating from "Log in" to "Connect" - there were conversations around this back when we put this in for ACI, concern then was that the top-level "Log In" button in the upper-right would have the same action so this button should use the same wording.

I'm assuming this was an update driven by design? If so then okay, just felt weird to see this get reversed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I talked to Emil about this - I think this should only affect the wording for the button on the runs page


cy.findByRole('dialog', { name: 'Log in to Cypress' }).as('logInModal').within(() => {
cy.findByRole('button', { name: 'Log in' }).click()
Expand Down
2 changes: 1 addition & 1 deletion packages/app/cypress/e2e/settings.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ describe('App: Settings without cloud', () => {
o.sinon.stub(ctx._apis.authApi, 'logIn')
})

cy.contains('button', 'Log in to Cypress Cloud').click()
cy.contains('button', 'Connect to Cypress Cloud').click()
cy.findByRole('dialog', { name: 'Log in to Cypress' }).within(() => {
cy.contains('button', 'Log in').click()
})
Expand Down
39 changes: 26 additions & 13 deletions packages/app/src/debug/DebugContainer.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { CloudRunStubs } from '@packages/graphql/test/stubCloudTypes'

describe('<DebugContainer />', () => {
context('empty states', () => {
const validateEmptyState = (expectedMessage: string) => {
const validateEmptyState = (expectedMessages: string[]) => {
cy.mountFragment(DebugSpecsFragmentDoc, {
render: (gqlVal) => {
return (
Expand All @@ -18,19 +18,24 @@ describe('<DebugContainer />', () => {
},
})

cy.findByTestId('debug-empty').contains(expectedMessage)
expectedMessages.forEach((message) => {
cy.findByTestId('debug-empty').contains(message)
})
astone123 marked this conversation as resolved.
Show resolved Hide resolved
}

it('shows not logged in', () => {
validateEmptyState(defaultMessages.debugPage.notLoggedIn)
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', () => {
it('is logged in with no project', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In ACI there were three separate "no project" states - are these all being rolled into this single "no project connected" condition?

  • No projectId at all
  • Invalid projectId (bad value, project since deleted, etc)
  • Unauthorized (valid projectId but user doesn't have access)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I believe here we're just doing one view that is based on loginConnectStore.project.isProjectConnected. @warrensplayer can you confirm?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@astone123 I am going to check with Peter and I will file a follow up issue if we need to add these additional states. I do not want to increase the scope of this issue beyond the original requirements.

const loginConnectStore = useLoginConnectStore()

loginConnectStore.setUserFlag('isLoggedIn', true)
loginConnectStore.setProjectFlag('isProjectConnected', false)

validateEmptyState(defaultMessages.debugPage.notConnected)
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', () => {
Expand All @@ -39,16 +44,24 @@ describe('<DebugContainer />', () => {
loginConnectStore.setUserFlag('isLoggedIn', true)
loginConnectStore.setProjectFlag('isProjectConnected', true)
cy.mountFragment(DebugSpecsFragmentDoc, {
render: (gqlVal) => {
return (
<DebugContainer
gql={gqlVal}
/>
)
},
render: (gqlVal) => <DebugContainer gql={gqlVal} />,
})

validateEmptyState([defaultMessages.debugPage.emptyStates.recordYourFirstRun, defaultMessages.debugPage.emptyStates.almostThere, defaultMessages.debugPage.emptyStates.noRunsTestMessage])
cy.findByDisplayValue('npx cypress run --record --key 2aaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa').should('be.visible')
})

it('errors', () => {
const loginConnectStore = useLoginConnectStore()

loginConnectStore.setUserFlag('isLoggedIn', true)
loginConnectStore.setProjectFlag('isProjectConnected', true)
cy.mountFragment(DebugSpecsFragmentDoc, {
render: (gqlVal) => <DebugContainer gql={gqlVal} showError={true} />,
})

cy.findByTestId('debug-empty').contains(defaultMessages.debugPage.noRuns)
cy.findByTestId('debug-empty').should('not.exist')
cy.findByTestId('debug-alert').should('be.visible')
})
})

Expand Down
38 changes: 16 additions & 22 deletions packages/app/src/debug/DebugContainer.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<template>
<div>
<DebugError
v-if="showError"
/>
<div
v-if="loginConnectStore.user.isLoggedIn && loginConnectStore.project.isProjectConnected && run"
v-else-if="loginConnectStore.user.isLoggedIn && loginConnectStore.project.isProjectConnected && run"
>
<DebugPageHeader
:gql="run"
Expand All @@ -15,21 +18,9 @@
v-else
data-cy="debug-empty"
>
<div
v-if="!loginConnectStore.user.isLoggedIn"
>
{{ t('debugPage.notLoggedIn') }}
</div>
<div
v-else-if="!loginConnectStore.project.isProjectConnected"
>
{{ t('debugPage.notConnected' ) }}
</div>
<div
v-else-if="!run"
>
{{ t('debugPage.noRuns') }}
</div>
<DebugNotLoggedIn v-if="!loginConnectStore.user.isLoggedIn" />
<DebugNoProject v-else-if="!loginConnectStore.project.isProjectConnected" />
<DebugNoRuns v-else-if="!run" />
</div>
</div>
</template>
Expand All @@ -41,11 +32,12 @@ import type { DebugSpecsFragment, TestingTypeEnum } from '../generated/graphql'
import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store'
import DebugPageHeader from './DebugPageHeader.vue'
import DebugSpecList from './DebugSpecList.vue'
import { useI18n } from 'vue-i18n'
import DebugNotLoggedIn from './empty/DebugNotLoggedIn.vue'
import DebugNoProject from './empty/DebugNoProject.vue'
import DebugNoRuns from './empty/DebugNoRuns.vue'
import DebugError from './empty/DebugError.vue'
import { specsList } from './utils/DebugMapping'

const { t } = useI18n()

gql`
fragment DebugLocalSpecs on Spec {
id
Expand Down Expand Up @@ -94,17 +86,19 @@ fragment DebugSpecs on Query {
`

const props = defineProps<{
gql: DebugSpecsFragment
gql?: DebugSpecsFragment
// This prop is just to stub the error state for now
showError?: boolean
astone123 marked this conversation as resolved.
Show resolved Hide resolved
}>()

const loginConnectStore = useLoginConnectStore()

const run = computed(() => {
return props.gql.currentProject?.cloudProject?.__typename === 'CloudProject' ? props.gql.currentProject.cloudProject.runByNumber : null
return props.gql?.currentProject?.cloudProject?.__typename === 'CloudProject' ? props.gql.currentProject.cloudProject.runByNumber : null
})

const debugSpecsArray = computed(() => {
if (run.value && props.gql.currentProject) {
if (run.value && props.gql?.currentProject) {
const specs = run.value.specs || []
const tests = run.value.testsForReview || []
const groups = run.value.groups || []
Expand Down
58 changes: 58 additions & 0 deletions packages/app/src/debug/empty/DebugEmptyStates.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import DebugNotLoggedIn from './DebugNotLoggedIn.vue'
import DebugNoProject from './DebugNoProject.vue'
import DebugNoRuns from './DebugNoRuns.vue'
import DebugLoading from './DebugLoading.vue'
import DebugError from './DebugError.vue'
import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store'

describe('Debug page empty states', () => {
context('not logged in', () => {
it('renders', () => {
const loginConnectStore = useLoginConnectStore()

// We need to set isLoggedIn so that CloudConnectButton shows the correct state
loginConnectStore.setUserFlag('isLoggedIn', false)

cy.mount(<DebugNotLoggedIn />)

cy.percySnapshot()
})
})

context('no project', () => {
it('renders', () => {
const loginConnectStore = useLoginConnectStore()

// We need to set isLoggedIn so that CloudConnectButton shows the correct state
loginConnectStore.setUserFlag('isLoggedIn', true)

cy.mount(<DebugNoProject />)

cy.percySnapshot()
})
})

context('no runs', () => {
it('renders', () => {
cy.mount(<DebugNoRuns />)

cy.percySnapshot()
})
})

context('loading', () => {
it('renders', () => {
cy.mount(<DebugLoading />)

cy.percySnapshot()
})
})

context('error', () => {
it('renders', () => {
cy.mount(<DebugError />)

cy.percySnapshot()
})
})
})
57 changes: 57 additions & 0 deletions packages/app/src/debug/empty/DebugEmptyView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<template>
<div class="flex flex-col my-45px items-center">
<div class="flex flex-col items-center justify-evenly">
<div><i-cy-box-open_x48 class="icon-dark-gray-500 icon-light-indigo-100" /></div>
<div class="flex flex-col mx-[20%] mt-25px mb-20px items-center">
<div class="font-medium my-5px text-center text-gray-900 text-18px">
{{ title }}
</div>
<div class="font-normal my-5px text-center leading-relaxed text-16px text-gray-600">
{{ description }}
</div>
</div>
<slot name="cta" />
</div>
<div class="flex flex-col my-40px w-full items-center">
<DebugTestLoadingContainer
width-class="w-[75%]"
dot-class="icon-light-gray-200"
:rows="loadingRows"
>
<template #header>
<div class="bg-white border rounded-md flex border-gray-100 w-max p-5px text-14px text-gray-700">
<div><i-cy-status-failed_x12 /></div>
<div class="bg-gray-700 h-1px mx-5px mt-7px w-5px" />
<div
v-if="exampleTestName"
class="bg-gray-100 h-13px mx-1 pb-1px w-1px"
/>
<div
v-if="exampleTestName"
class="mx-1 text-14px text-gray-700"
>
{{ exampleTestName }}
</div>
</div>
</template>
</DebugTestLoadingContainer>
</div>
</div>
</template>

<script lang="ts" setup>
import DebugTestLoadingContainer from './DebugTestLoadingContainer.vue'

defineProps<{
title: string
description?: string
exampleTestName?: string
}>()

const loadingRows = [
['w-40px', 'w-[40%]'],
['w-40px', 'w-[50%]'],
['w-40px', 'w-[65%]'],
]

</script>
27 changes: 27 additions & 0 deletions packages/app/src/debug/empty/DebugError.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<template>
<div class="m-25px">
<Alert
data-cy="debug-alert"
status="warning"
:title="t('debugPage.emptyStates.gitRepositoryNotDetected')"
:icon="WarningIcon"
dismissible
>
<p>{{ t('debugPage.emptyStates.ensureGitSetupCorrectly') }}</p>
</Alert>
</div>
<DebugEmptyView
:title="t('debugPage.emptyStates.debugDirectlyInCypress')"
:description="t('debugPage.emptyStates.reviewRerunAndDebug')"
/>
</template>

<script lang="ts" setup>
import DebugEmptyView from './DebugEmptyView.vue'
import Alert from '@packages/frontend-shared/src/components/Alert.vue'
import WarningIcon from '~icons/cy/warning_x16.svg'
import { useI18n } from '@cy/i18n'

const { t } = useI18n()

</script>
Loading