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 10 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
4 changes: 2 additions & 2 deletions packages/app/cypress/e2e/specs_list_latest_runs.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ describe('App/Cloud Integration - Latest runs and Average duration', { viewportW
cy.get('.v-popper__popper--shown')
.should('contain', 'Connect to Cypress Cloud to see the status of your latest runs')
.find('button')
.should('have.text', 'Log in to Cypress Cloud')
.should('have.text', 'Connect to Cypress Cloud')
.click()

cy.findByRole('dialog', { name: 'Log in to Cypress' }).within(() => {
Expand All @@ -217,7 +217,7 @@ describe('App/Cloud Integration - Latest runs and Average duration', { viewportW
cy.get('.v-popper__popper--shown')
.should('contain', 'Connect to Cypress Cloud to see the average spec durations of your latest runs')
.find('button')
.should('have.text', 'Log in to Cypress Cloud')
.should('have.text', 'Connect to Cypress Cloud')
.click()

cy.findByRole('dialog', { name: 'Log in to Cypress' }).within(() => {
Expand Down
43 changes: 37 additions & 6 deletions packages/app/src/debug/DebugContainer.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,36 @@ 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 (
<DebugContainer
loading={false}
gql={gqlVal}
/>
)
},
})

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 @@ -42,13 +48,37 @@ describe('<DebugContainer />', () => {
render: (gqlVal) => {
return (
<DebugContainer
loading={false}
gql={gqlVal}
/>
)
},
})

cy.findByTestId('debug-empty').contains(defaultMessages.debugPage.emptyStates.recordYourFirstRun)
cy.findByTestId('debug-empty').contains(defaultMessages.debugPage.emptyStates.almostThere)
cy.findByTestId('debug-empty').contains(defaultMessages.debugPage.emptyStates.noRunsTestMessage)
astone123 marked this conversation as resolved.
Show resolved Hide resolved
cy.findByDisplayValue('npx cypress run --record --key 2aaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa').should('be.visible')
})

it('is loading', () => {
const loginConnectStore = useLoginConnectStore()

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

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

Expand All @@ -70,6 +100,7 @@ describe('<DebugContainer />', () => {
render: (gqlVal) => {
return (
<DebugContainer
loading={false}
astone123 marked this conversation as resolved.
Show resolved Hide resolved
gql={gqlVal}
/>
)
Expand Down
33 changes: 13 additions & 20 deletions packages/app/src/debug/DebugContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,16 @@
:specs="debugSpecsArray"
/>
</div>
<div v-else-if="loading">
astone123 marked this conversation as resolved.
Show resolved Hide resolved
<DebugLoading />
</div>
<div
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 } 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 './DebugNotLoggedIn.vue'
import DebugNoProject from './DebugNoProject.vue'
import DebugNoRuns from './DebugNoRuns.vue'
import DebugLoading from './DebugLoading.vue'
import { specsList } from './utils/DebugMapping'

const { t } = useI18n()

gql`
fragment DebugSpecs on Query {
currentProject {
Expand Down Expand Up @@ -77,13 +69,14 @@ fragment DebugSpecs on Query {
`

const props = defineProps<{
gql: DebugSpecsFragment
gql?: DebugSpecsFragment
loading: boolean
}>()

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(() => {
Expand Down
53 changes: 53 additions & 0 deletions packages/app/src/debug/DebugEmptyStates.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import DebugNotLoggedIn from './DebugNotLoggedIn.vue'
import DebugNoProject from './DebugNoProject.vue'
import DebugNoRuns from './DebugNoRuns.vue'
import DebugLoading from './DebugLoading.vue'
import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store'

describe('Debug page empty states', () => {
beforeEach(() => {
cy.viewport(900, 800)
astone123 marked this conversation as resolved.
Show resolved Hide resolved
})

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()
})
})
})
75 changes: 75 additions & 0 deletions packages/app/src/debug/DebugEmptyView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<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">
<div class="border-t border-l border-r rounded-t-md bg-gray-50 border-0 border-gray-100 py-20px px-15px w-[75%]">
<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="mx-1">
-
</div>
<div
v-if="exampleTestName"
class="mx-1 pb-1px text-gray-100"
>
|
astone123 marked this conversation as resolved.
Show resolved Hide resolved
</div>
<div
v-if="exampleTestName"
class="mx-1 text-14px text-gray-700"
>
{{ exampleTestName }}
</div>
</div>
</div>
<div
v-for="(row, i) in loadingRows"
:key="i"
class="bg-white border flex border-b-0 border-gray-100 py-5 px-17px w-[75%] items-center overflow-hidden last:border-b last:rounded-b-md"
>
<div>
<i-cy-dot-solid_x4 class="icon-light-gray-200" />
</div>
<div class="ml-10px">
<div class="rounded-full bg-gray-50 h-16px w-40px" />
</div>
<div class="ml-10px">
<i-cy-chevron-right_x16 class="icon-dark-gray-200" />
</div>
<div class="ml-10px">
<div
class="rounded-full bg-gray-50 h-16px"
:class="row.widthClass"
/>
</div>
</div>
</div>
</div>
</template>

<script lang="ts" setup>

defineProps<{
title: String
astone123 marked this conversation as resolved.
Show resolved Hide resolved
description?: String
exampleTestName?: String
}>()

const loadingRows = [
{ widthClass: 'w-341px' },
{ widthClass: 'w-407px' },
{ widthClass: 'w-505px' },
astone123 marked this conversation as resolved.
Show resolved Hide resolved
]

</script>
Loading