Skip to content

Commit

Permalink
feat: IATR non-failure run statuses (#25286)
Browse files Browse the repository at this point in the history
Co-authored-by: Rocky <25568640+rockindahizzy@users.noreply.github.com>
  • Loading branch information
astone123 and rockindahizzy authored Dec 29, 2022
1 parent 9008365 commit bad5093
Show file tree
Hide file tree
Showing 16 changed files with 612 additions and 751 deletions.
52 changes: 52 additions & 0 deletions packages/app/src/debug/DebugCancelledAlert.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<template>
<Alert
:title="t('debugPage.manuallyCancelled')"
status="warning"
:icon="ErrorOutlineIcon"
class="flex flex-col mb-24px w-full"
>
<div class="flex items-center">
<div>{{ t('debugPage.specsSkipped', {n: totalSpecs, totalSkippedSpecs}) }}</div>
<template v-if="cancellation.cancelledBy?.email && cancellation.cancelledBy.fullName">
<div class="rounded-full font-semibold bg-orange-500 h-3px mx-10px w-3px" />
<div class="flex items-center">
<UserAvatar
:email="cancellation.cancelledBy.email"
class="h-20px mr-7px w-20px"
data-cy="cancelled-by-user-avatar"
/>
<div>{{ cancellation.cancelledBy.fullName }}</div>
</div>
</template>
<template v-if="cancellation.cancelledAt">
<div class="rounded-full font-semibold bg-orange-500 h-3px mx-10px w-3px" />
<div>
{{ dayjs(cancellation.cancelledAt).local().format('MMM D, YYYY h:mm A') }}
</div>
</template>
</div>
</Alert>
</template>
<script lang="ts" setup>
import UserAvatar from '@packages/frontend-shared/src/gql-components/topnav/UserAvatar.vue'
import ErrorOutlineIcon from '~icons/cy/status-errored-outline_x16.svg'
import Alert from '@cy/components/Alert.vue'
import { dayjs } from '../runs/utils/day'
import { useI18n } from '@cy/i18n'
const { t } = useI18n()
defineProps<{
totalSpecs: number
totalSkippedSpecs: number
cancellation: {
cancelledAt?: string | null
cancelledBy: {
fullName?: string | null
email?: string | null
} | null
}
}>()
</script>
104 changes: 103 additions & 1 deletion packages/app/src/debug/DebugContainer.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { specsList } from './utils/DebugMapping'
import { CloudRunStubs } from '@packages/graphql/test/stubCloudTypes'

describe('<DebugContainer />', () => {
context('empty states', () => {
describe('empty states', () => {
const validateEmptyState = (expectedMessages: string[]) => {
cy.mountFragment(DebugSpecsFragmentDoc, {
render: (gqlVal) => {
Expand Down Expand Up @@ -65,6 +65,108 @@ describe('<DebugContainer />', () => {
})
})

describe('run states', { viewportWidth: 900 }, () => {
beforeEach(() => {
const loginConnectStore = useLoginConnectStore()

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

function mountTestRun (runName: string) {
cy.mountFragment(DebugSpecsFragmentDoc, {
onResult: (result) => {
if (result.currentProject?.cloudProject?.__typename === 'CloudProject') {
const test = result.currentProject.cloudProject.runByNumber
const other = CloudRunStubs[runName] as typeof test

result.currentProject.cloudProject.runByNumber = other
}
},
render: (gqlVal) => {
return (
<DebugContainer
gql={gqlVal}
/>
)
},
})
}

context('passed', () => {
it('renders', () => {
mountTestRun('allPassing')

cy.contains('Well Done!').should('be.visible')

cy.percySnapshot()
})
})

context('errored', () => {
it('renders', () => {
mountTestRun('allSkipped')

cy.contains('The browser server never connected.').should('be.visible')
cy.contains('2 of 3 specs skipped').should('be.visible')

cy.percySnapshot()
})
})

context('no tests', () => {
it('renders', () => {
mountTestRun('noTests')

cy.contains('Run has no tests').should('be.visible')

cy.percySnapshot()
})
})

context('timed out', () => {
it('renders with CI information', () => {
mountTestRun('timedOutWithCi')

cy.contains('Circle CI #1234').should('have.attr', 'href', 'https://circleci.com').should('be.visible')
cy.contains('Archive this run to remove it').should('be.visible')

cy.percySnapshot()
})

it('renders without CI information', () => {
mountTestRun('timedOutWithoutCi')

cy.contains('Circle CI #1234').should('not.exist')
cy.contains('Archive this run to remove it').should('be.visible')

cy.percySnapshot()
})
})

context('over limit', () => {
it('renders', () => {
mountTestRun('overLimit')

cy.findByRole('link', { name: 'Contact admin' }).should('have.attr', 'href', 'http://localhost:3000?utmMedium=Debug+Tab&utmSource=Binary%3A+Launchpad')

cy.percySnapshot()
})
})

context('cancelled', () => {
it('renders', () => {
mountTestRun('cancelled')

cy.findByTestId('cancelled-by-user-avatar').should('be.visible')
cy.contains('2 of 3 specs skipped').should('be.visible')
cy.contains('Test Tester').should('be.visible')

cy.percySnapshot()
})
})
})

describe('render specs and tests', () => {
it('renders data when logged in and connected', () => {
const loginConnectStore = useLoginConnectStore()
Expand Down
41 changes: 38 additions & 3 deletions packages/app/src/debug/DebugContainer.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
<template>
<div>
<div class="h-full">
<DebugError
v-if="showError"
/>
<div
v-else-if="loginConnectStore.user.isLoggedIn && loginConnectStore.project.isProjectConnected && run"
v-else-if="loginConnectStore.user.isLoggedIn && loginConnectStore.project.isProjectConnected && run?.status"
class="flex flex-col h-full"
>
<DebugPageHeader
:gql="run"
:commits-ahead="0"
/>
<DebugPageDetails
v-if="shouldDisplayDetails(run.status)"
:status="run.status"
:specs="run.specs"
:cancellation="{ cancelledAt: run.cancelledAt, cancelledBy: run.cancelledBy }"
:is-hidden-by-usage-limits="run.isHiddenByUsageLimits"
:over-limit-action-type="run.overLimitActionType"
:over-limit-action-url="run.overLimitActionUrl"
:ci="run.ci"
:errors="run.errors"
/>
<DebugSpecList
v-if="run.totalFailed && shouldDisplaySpecsList(run.status)"
:specs="debugSpecsArray"
/>
</div>
Expand All @@ -28,10 +41,11 @@
<script setup lang="ts">
import { gql } from '@urql/vue'
import { computed } from '@vue/reactivity'
import type { DebugSpecsFragment, TestingTypeEnum } from '../generated/graphql'
import type { CloudRunStatus, 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 DebugPageDetails from './DebugPageDetails.vue'
import DebugNotLoggedIn from './empty/DebugNotLoggedIn.vue'
import DebugNoProject from './empty/DebugNoProject.vue'
import DebugNoRuns from './empty/DebugNoRuns.vue'
Expand All @@ -56,11 +70,24 @@ fragment DebugSpecs on Query {
id
runByNumber(runNumber: 11) {
...DebugPage
cancelledBy {
id
fullName
email
}
cancelledAt
id
runNumber
errors
status
overLimitActionType
overLimitActionUrl
isHiddenByUsageLimits
totalTests
ci {
id
...DebugPageDetails_cloudCiBuildInfo
}
testsForReview {
id
...DebugSpecListTests
Expand Down Expand Up @@ -97,6 +124,14 @@ const run = computed(() => {
return props.gql?.currentProject?.cloudProject?.__typename === 'CloudProject' ? props.gql.currentProject.cloudProject.runByNumber : null
})
function shouldDisplayDetails (status: CloudRunStatus) {
return !['RUNNING', 'FAILED'].includes(status)
}
function shouldDisplaySpecsList (status: CloudRunStatus) {
return ['ERRORED', 'CANCELLED', 'TIMEDOUT', 'FAILED'].includes(status)
}
const debugSpecsArray = computed(() => {
if (run.value && props.gql?.currentProject) {
const specs = run.value.specs || []
Expand Down
40 changes: 40 additions & 0 deletions packages/app/src/debug/DebugErrored.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<template>
<Alert
:title="t('debugPage.incomplete')"
status="warning"
:icon="ErrorOutlineIcon"
class="flex flex-col mb-24px w-full"
>
<div class="ml-5px">
<ul
v-for="(error, index) in errors"
:key="index"
class="list-disc ml-25px"
>
<li><pre>{{ error }}</pre></li>
</ul>
</div>
<div class="mt-20px">
{{ t('debugPage.specsSkipped', {n: totalSpecs, totalSkippedSpecs}) }}
</div>
</Alert>
</template>

<script lang="ts" setup>
import ErrorOutlineIcon from '~icons/cy/status-errored-outline_x16.svg'
import Alert from '@cy/components/Alert.vue'
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import { useI18n } from '@cy/i18n'
const { t } = useI18n()
dayjs.extend(utc)
defineProps<{
errors?: readonly string[]
totalSpecs: number
totalSkippedSpecs: number
}>()
</script>
19 changes: 19 additions & 0 deletions packages/app/src/debug/DebugNoTests.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<Alert
:title="t('debugPage.incomplete')"
status="warning"
:icon="ErrorOutlineIcon"
class="flex flex-col mb-24px w-full"
>
{{ t('debugPage.runHasNoTests') }}
</Alert>
</template>

<script lang="ts" setup>
import ErrorOutlineIcon from '~icons/cy/status-errored-outline_x16.svg'
import Alert from '@cy/components/Alert.vue'
import { useI18n } from '@cy/i18n'
const { t } = useI18n()
</script>
41 changes: 41 additions & 0 deletions packages/app/src/debug/DebugOverLimit.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<template>
<div
class="flex flex-col items-center"
>
<LockedProject class="icon-dark-gray-500" />
<span class="font-medium mt-24px text-gray-900">
{{ t('debugPage.overPlanLimit') }}
</span>
<span class="mt-10px text-gray-600">{{ t('debugPage.reachedMonthlyUsage') }}</span>
<span class="text-gray-600">{{ t('debugPage.upgradeYourPlan') }}</span>
<Button
size="lg"
class="mt-25px"
:href="actionUrl"
>
{{ overLimitActionType === 'CONTACT_ADMIN' ? t('debugPage.contactAdmin') : t('debugPage.upgradePlan') }}
</Button>
</div>
</template>

<script lang="ts" setup>
import LockedProject from '~icons/cy/locked-project_x48.svg'
import Button from '@packages/frontend-shared/src/components/Button.vue'
import type { OverLimitActionTypeEnum } from '../generated/graphql'
import { getUtmSource } from '@packages/frontend-shared/src/utils/getUtmSource'
import { useI18n } from '@cy/i18n'
import { getUrlWithParams } from '@packages/frontend-shared/src/utils/getUrlWithParams'
import { computed } from '@vue/reactivity'
const { t } = useI18n()
const props = defineProps<{
overLimitActionType: OverLimitActionTypeEnum
overLimitActionUrl: string
}>()
const actionUrl = computed(() => {
return getUrlWithParams({ url: props.overLimitActionUrl, params: { utmMedium: 'Debug Tab', utmSource: getUtmSource() } })
})
</script>
Loading

0 comments on commit bad5093

Please sign in to comment.