diff --git a/README.md b/README.md
index 5ed74849fdc5..d10a70893421 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,11 @@
+
diff --git a/packages/app/src/settings/SettingsContainer.cy.tsx b/packages/app/src/settings/SettingsContainer.cy.tsx
index 6638c6e27922..76a766cef461 100644
--- a/packages/app/src/settings/SettingsContainer.cy.tsx
+++ b/packages/app/src/settings/SettingsContainer.cy.tsx
@@ -2,17 +2,19 @@ import { SettingsContainerFragmentDoc } from '../generated/graphql-test'
import { defaultMessages } from '@cy/i18n'
import SettingsContainer from './SettingsContainer.vue'
+const mountSettingsContainer = () => cy.mountFragment(SettingsContainerFragmentDoc, { render: (gql) =>
})
+
+beforeEach(() => mountSettingsContainer())
+
describe('
', { viewportHeight: 800, viewportWidth: 900 }, () => {
- const mountSettingsContainer = () => cy.mountFragment(SettingsContainerFragmentDoc, { render: (gql) =>
})
+ it('renders sections collapsed by default', () => {
+ cy.findByTestId('settings').should('be.visible')
+ cy.findByTestId('setting-expanded-container').should('not.exist')
- it('renders', () => {
- mountSettingsContainer()
cy.percySnapshot()
})
it('expands and collapses project settings', () => {
- mountSettingsContainer()
-
cy.contains('Project Settings').click()
cy.findByText(defaultMessages.settingsPage.experiments.title).scrollIntoView().should('be.visible')
@@ -25,8 +27,6 @@ describe('
', { viewportHeight: 800, viewportWidth: 900 }, (
})
it('expands and collapses device settings', () => {
- mountSettingsContainer()
-
cy.contains('Device Settings').click()
cy.findByText(defaultMessages.settingsPage.editor.title).should('be.visible')
@@ -40,8 +40,6 @@ describe('
', { viewportHeight: 800, viewportWidth: 900 }, (
})
it('expands and collapses cloud settings', () => {
- mountSettingsContainer()
-
cy.contains('Dashboard Settings').click()
cy.findByText(defaultMessages.settingsPage.projectId.title).scrollIntoView().should('be.visible')
@@ -52,7 +50,6 @@ describe('
', { viewportHeight: 800, viewportWidth: 900 }, (
})
it('renders footer with CTA button', () => {
- mountSettingsContainer()
cy.contains('p', defaultMessages.settingsPage.footer.text.replace('{testingType}', 'E2E'))
cy.contains('a', defaultMessages.settingsPage.footer.button)
.should('have.attr', 'href', defaultMessages.settingsPage.footer.buttonLink)
diff --git a/packages/app/src/specs/InlineSpecListHeader.cy.tsx b/packages/app/src/specs/InlineSpecListHeader.cy.tsx
index 8ee30fd332de..3d31fa26f187 100644
--- a/packages/app/src/specs/InlineSpecListHeader.cy.tsx
+++ b/packages/app/src/specs/InlineSpecListHeader.cy.tsx
@@ -10,7 +10,6 @@ describe('InlineSpecListHeader', () => {
cy.wrap(search).as('search')
const methods = {
- search: search.value,
'onUpdate:search': (val: string) => {
search.value = val
},
@@ -19,7 +18,7 @@ describe('InlineSpecListHeader', () => {
cy.mount(() =>
(
-
+
))
}
@@ -28,6 +27,7 @@ describe('InlineSpecListHeader', () => {
const searchString = 'my/component.cy.tsx'
cy.findByLabelText(defaultMessages.specPage.searchPlaceholder)
+ // `force` necessary due to the field label being overlaid on top of the input
.type(searchString, { delay: 0, force: true })
.get('@search').its('value').should('eq', searchString)
})
@@ -40,6 +40,21 @@ describe('InlineSpecListHeader', () => {
.should('have.been.called')
})
+ it('clears search field when clear button is clicked', () => {
+ mountWithResultCount(0)
+
+ cy.findByTestId('clear-search-button')
+ .should('not.exist')
+
+ cy.findByLabelText(defaultMessages.specPage.searchPlaceholder)
+ // `force` necessary due to the field label being overlaid on top of the input
+ .type('abcd', { delay: 0, force: true })
+ .get('@search').its('value').should('eq', 'abcd')
+
+ cy.findByTestId('clear-search-button').click()
+ cy.get('@search').its('value').should('eq', '')
+ })
+
it('exposes the result count correctly to assistive tech', () => {
mountWithResultCount(0)
cy.contains('No Matches')
diff --git a/packages/app/src/specs/InlineSpecListHeader.vue b/packages/app/src/specs/InlineSpecListHeader.vue
index ac9a9617a760..b388a71e9567 100644
--- a/packages/app/src/specs/InlineSpecListHeader.vue
+++ b/packages/app/src/specs/InlineSpecListHeader.vue
@@ -3,7 +3,7 @@
class="border-b-1 border-gray-900 h-64px mx-16px grid gap-8px grid-cols-[minmax(0,1fr),24px] pointer-cursor items-center"
>
{{ t('specPage.searchPlaceholder') }}
+
+
+
- )
+
+ )
+ })
+}
+
+describe('
', () => {
+ context('runs scenario 1', () => {
+ beforeEach(() => {
+ const runs = fakeRuns(['PASSED', 'FAILED', 'CANCELLED', 'ERRORED'])
+
+ mountWithRuns(runs)
})
- cy.findByTestId('run-status-dots').trigger('mouseenter')
- cy.get('.v-popper__popper--shown').contains('spec.cy.ts')
- cy.findAllByTestId('run-status-dot-0').should('have.class', 'icon-light-orange-400')
- cy.findAllByTestId('run-status-dot-1').should('have.class', 'icon-light-gray-300')
- cy.findAllByTestId('run-status-dot-2').should('have.class', 'icon-light-red-400')
- cy.findAllByTestId('run-status-dot-latest').should('not.have.class', 'animate-spin')
+ it('renders as expected', () => {
+ cy.findByTestId('run-status-dots').trigger('mouseenter')
+ cy.get('.v-popper__popper--shown').contains('spec.cy.ts')
+ cy.findAllByTestId('run-status-dot-0').should('have.class', 'icon-light-orange-400')
+ cy.findAllByTestId('run-status-dot-1').should('have.class', 'icon-light-gray-300')
+ cy.findAllByTestId('run-status-dot-2').should('have.class', 'icon-light-red-400')
+ cy.findAllByTestId('run-status-dot-latest').should('not.have.class', 'animate-spin')
+ })
})
- it('mounts correctly for example scenario 2', () => {
- const runs = fakeRuns(['NOTESTS', 'OVERLIMIT', 'RUNNING', 'TIMEDOUT'])
-
- cy.mount(() => {
- const gql: RunStatusDotsFragment = {
- id: 'id',
- data: {
- __typename: 'CloudProjectSpec',
- id: 'id',
- retrievedAt: new Date().toISOString(),
- specRuns: {
- nodes: [
- ...runs as any, // suppress TS compiler
- ],
- },
- },
- }
-
- return (
-
- )
+ context('runs scenario 2', () => {
+ beforeEach(() => {
+ const runs = fakeRuns(['NOTESTS', 'UNCLAIMED', 'RUNNING', 'TIMEDOUT'])
+
+ mountWithRuns(runs)
})
- cy.findByTestId('run-status-dots').trigger('mouseenter')
- cy.get('.v-popper__popper--shown').contains('spec.cy.ts')
- cy.findAllByTestId('run-status-dot-0').should('have.class', 'icon-light-orange-400')
- cy.findAllByTestId('run-status-dot-1').should('have.class', 'icon-light-indigo-400')
- cy.findAllByTestId('run-status-dot-2').should('have.class', 'icon-light-orange-400')
- cy.findAllByTestId('run-status-dot-latest').should('not.have.class', 'animate-spin')
+ it('renders as expected', () => {
+ cy.findByTestId('run-status-dots').trigger('mouseenter')
+ cy.get('.v-popper__popper--shown').contains('spec.cy.ts')
+ cy.findAllByTestId('run-status-dot-0').should('have.class', 'icon-light-orange-400')
+ cy.findAllByTestId('run-status-dot-1').should('have.class', 'icon-light-indigo-400')
+ cy.findAllByTestId('run-status-dot-2').should('have.class', 'icon-light-gray-400')
+ cy.findAllByTestId('run-status-dot-latest').should('not.have.class', 'animate-spin')
+ })
})
- it('mounts correctly for example scenario 3', () => {
- const runs = fakeRuns(['RUNNING'])
-
- cy.mount(() => {
- const gql: RunStatusDotsFragment = {
- id: 'id',
- data: {
- __typename: 'CloudProjectSpec',
- id: 'id',
- retrievedAt: new Date().toISOString(),
- specRuns: {
- nodes: [
- ...runs as any, // suppress TS compiler
- ],
- },
- },
- }
-
- return (
-
- )
+ context('single RUNNING status', () => {
+ beforeEach(() => {
+ const runs = fakeRuns(['RUNNING'])
+
+ mountWithRuns(runs)
})
- cy.findByTestId('run-status-dots').trigger('mouseenter')
- cy.get('.v-popper__popper--shown').contains('spec.cy.ts')
- cy.findAllByTestId('run-status-dot-0').should('have.class', 'icon-light-gray-300')
- cy.findAllByTestId('run-status-dot-1').should('have.class', 'icon-light-gray-300')
- cy.findAllByTestId('run-status-dot-2').should('have.class', 'icon-light-gray-300')
- cy.findAllByTestId('run-status-dot-latest').should('have.class', 'animate-spin')
+ it('renders as expected', () => {
+ cy.findByTestId('run-status-dots').trigger('mouseenter')
+ cy.get('.v-popper__popper--shown').contains('spec.cy.ts')
+ cy.findAllByTestId('run-status-dot-0').should('have.class', 'icon-light-gray-300')
+ cy.findAllByTestId('run-status-dot-1').should('have.class', 'icon-light-gray-300')
+ cy.findAllByTestId('run-status-dot-2').should('have.class', 'icon-light-gray-300')
+ cy.findAllByTestId('run-status-dot-latest').should('have.class', 'animate-spin')
+ })
})
- it('renders placeholder without tooltip or link', () => {
- cy.mount(() => {
- const gql: RunStatusDotsFragment = {
- id: 'id',
- data: {
- __typename: 'CloudProjectSpec',
- id: 'id',
- retrievedAt: new Date().toISOString(),
- specRuns: {
- nodes: [],
- },
- },
- }
-
- return (
-
- )
+ context('single UNCLAIMED status', () => {
+ beforeEach(() => {
+ const runs = fakeRuns(['UNCLAIMED'])
+
+ mountWithRuns(runs)
+ })
+
+ it('renders as expected', () => {
+ cy.findByTestId('run-status-dots').trigger('mouseenter')
+ cy.get('.v-popper__popper--shown').contains('spec.cy.ts')
+ cy.findAllByTestId('run-status-dot-0').should('have.class', 'icon-light-gray-300')
+ cy.findAllByTestId('run-status-dot-1').should('have.class', 'icon-light-gray-300')
+ cy.findAllByTestId('run-status-dot-2').should('have.class', 'icon-light-gray-300')
+ cy.findAllByTestId('run-status-dot-latest').should('not.have.class', 'animate-spin')
})
+ })
- cy.findByTestId('external').should('not.exist')
- cy.findByTestId('run-status-dots').trigger('mouseenter')
- cy.get('.v-popper__popper--shown').should('not.exist')
+ context('no runs', () => {
+ beforeEach(() => {
+ mountWithRuns([])
+ })
+
+ it('renders placeholder without tooltip or link', () => {
+ cy.findByTestId('external').should('not.exist')
+ cy.findByTestId('run-status-dots').trigger('mouseenter')
+ cy.get('.v-popper__popper--shown').should('not.exist')
+ })
+ })
+
+ context('unknown/unhandled statuses', () => {
+ beforeEach(() => {
+ const runs = fakeRuns(fill(['', '', '', ''], 'FAKE_UNKNOWN_STATUS' as any))
+
+ mountWithRuns(runs)
+ })
+
+ it('renders as expected', () => {
+ cy.findByTestId('run-status-dots').trigger('mouseenter')
+ cy.get('.v-popper__popper--shown').contains('spec.cy.ts')
+ cy.findAllByTestId('run-status-dot-0').should('have.class', 'icon-light-gray-300')
+ cy.findAllByTestId('run-status-dot-1').should('have.class', 'icon-light-gray-300')
+ cy.findAllByTestId('run-status-dot-2').should('have.class', 'icon-light-gray-300')
+ cy.findAllByTestId('run-status-dot-latest').should('not.have.class', 'animate-spin')
+ })
})
})
diff --git a/packages/app/src/specs/RunStatusDots.vue b/packages/app/src/specs/RunStatusDots.vue
index 72b3021a0122..ffce6628cb13 100644
--- a/packages/app/src/specs/RunStatusDots.vue
+++ b/packages/app/src/specs/RunStatusDots.vue
@@ -73,6 +73,7 @@ import ErroredIcon from '~icons/cy/errored-solid_x16.svg'
import FailedIcon from '~icons/cy/failed-solid_x16.svg'
import PassedIcon from '~icons/cy/passed-solid_x16.svg'
import PlaceholderIcon from '~icons/cy/placeholder-solid_x16.svg'
+import QueuedIcon from '~icons/cy/queued-solid_x16.svg'
import RunningIcon from '~icons/cy/running-outline_x16.svg'
import SpecRunSummary from './SpecRunSummary.vue'
import { gql } from '@urql/vue'
@@ -155,9 +156,9 @@ const dotClasses = computed(() => {
case 'FAILED':
return 'icon-light-red-400'
case 'ERRORED':
- case 'OVERLIMIT':
case 'TIMEDOUT':
return 'icon-light-orange-400'
+ case 'UNCLAIMED':
case 'NOTESTS':
return 'icon-light-gray-400'
case 'CANCELLED':
@@ -183,10 +184,11 @@ const latestDot = computed(() => {
return { icon: PassedIcon, spin: false, status }
case 'RUNNING':
return { icon: RunningIcon, spin: true, status }
+ case 'UNCLAIMED':
+ return { icon: QueuedIcon, spin: false, status }
case 'FAILED':
return { icon: FailedIcon, spin: false, status }
case 'ERRORED':
- case 'OVERLIMIT':
case 'TIMEDOUT':
return { icon: ErroredIcon, spin: false, status }
case 'NOTESTS':
diff --git a/packages/app/src/specs/SpecHeaderCloudDataTooltip.cy.tsx b/packages/app/src/specs/SpecHeaderCloudDataTooltip.cy.tsx
index 608541d186c7..e1f3c002ff64 100644
--- a/packages/app/src/specs/SpecHeaderCloudDataTooltip.cy.tsx
+++ b/packages/app/src/specs/SpecHeaderCloudDataTooltip.cy.tsx
@@ -1,4 +1,4 @@
-import { SpecHeaderCloudDataTooltipFragmentDoc } from '../generated/graphql-test'
+import { SpecHeaderCloudDataTooltipFragmentDoc, SpecHeaderCloudDataTooltip_RequestAccessDocument } from '../generated/graphql-test'
import SpecHeaderCloudDataTooltip from './SpecHeaderCloudDataTooltip.vue'
import { get, set } from 'lodash'
import { defaultMessages } from '@cy/i18n'
@@ -136,11 +136,29 @@ describe('SpecHeaderCloudDataTooltip', () => {
.should('be.visible')
.and('contain', get(defaultMessages, msgKeys.noAccess).replace('{0}', get(defaultMessages, msgKeys.docs)))
+ cy.percySnapshot()
+ })
+
+ it('should update to "Request Sent" when button is triggered', () => {
+ cy.stubMutationResolver(SpecHeaderCloudDataTooltip_RequestAccessDocument, (defineResult) => {
+ return defineResult({
+ cloudProjectRequestAccess: {
+ __typename: 'CloudProjectUnauthorized',
+ message: 'msg',
+ hasRequestedAccess: true,
+ },
+ })
+ })
+
+ cy.get('.v-popper').trigger('mouseenter')
+
cy.findByTestId('request-access-button')
.should('be.visible')
.click()
- cy.percySnapshot()
+ cy.findByTestId('access-requested-button')
+ .should('be.visible')
+ .should('be.disabled')
})
})
diff --git a/packages/app/src/specs/SpecHeaderCloudDataTooltip.vue b/packages/app/src/specs/SpecHeaderCloudDataTooltip.vue
index c22a0bed5638..e74bd36cf3b5 100644
--- a/packages/app/src/specs/SpecHeaderCloudDataTooltip.vue
+++ b/packages/app/src/specs/SpecHeaderCloudDataTooltip.vue
@@ -100,9 +100,10 @@ import ConnectIcon from '~icons/cy/chain-link_x16.svg'
import UserOutlineIcon from '~icons/cy/user-outline_x16.svg'
import SendIcon from '~icons/cy/paper-airplane_x16.svg'
import ExternalLink from '@cy/gql-components/ExternalLink.vue'
-import { RunsErrorRenderer_RequestAccessDocument, SpecHeaderCloudDataTooltipFragment } from '../generated/graphql'
+import type { SpecHeaderCloudDataTooltipFragment } from '../generated/graphql'
+import { SpecHeaderCloudDataTooltip_RequestAccessDocument } from '../generated/graphql'
import { useI18n } from '@cy/i18n'
-import { computed } from 'vue'
+import { computed, onMounted, ref } from 'vue'
import { gql, useMutation } from '@urql/vue'
const { t } = useI18n()
@@ -138,6 +139,26 @@ fragment SpecHeaderCloudDataTooltip on Query {
}
`
+gql`
+mutation SpecHeaderCloudDataTooltip_RequestAccess( $projectId: String! ) {
+ cloudProjectRequestAccess(projectSlug: $projectId) {
+ __typename
+ ... on CloudProjectUnauthorized {
+ message
+ hasRequestedAccess
+ }
+ }
+}
+`
+
+const hasRequestedAccess = ref(false)
+
+onMounted(() => {
+ if (props.gql.currentProject?.cloudProject?.__typename === 'CloudProjectUnauthorized') {
+ hasRequestedAccess.value = props.gql.currentProject.cloudProject.hasRequestedAccess ?? false
+ }
+})
+
const projectConnectionStatus = computed(() => {
if (!props.gql.cloudViewer) return 'LOGGED_OUT'
@@ -146,7 +167,7 @@ const projectConnectionStatus = computed(() => {
if (props.gql.currentProject?.cloudProject?.__typename === 'CloudProjectNotFound') return 'NOT_FOUND'
if (props.gql.currentProject?.cloudProject?.__typename === 'CloudProjectUnauthorized') {
- if (props.gql.currentProject.cloudProject.hasRequestedAccess) {
+ if (hasRequestedAccess.value) {
return 'ACCESS_REQUESTED'
}
@@ -156,13 +177,19 @@ const projectConnectionStatus = computed(() => {
return 'CONNECTED'
})
-const requestAccessMutation = useMutation(RunsErrorRenderer_RequestAccessDocument)
+const requestAccessMutation = useMutation(SpecHeaderCloudDataTooltip_RequestAccessDocument)
-function requestAccess () {
+async function requestAccess () {
const projectId = props.gql.currentProject?.projectId
if (projectId) {
- requestAccessMutation.executeMutation({ projectId })
+ const result = await requestAccessMutation.executeMutation({ projectId })
+
+ if (result.data?.cloudProjectRequestAccess?.__typename === 'CloudProjectUnauthorized') {
+ hasRequestedAccess.value = result.data.cloudProjectRequestAccess.hasRequestedAccess ?? false
+ } else {
+ hasRequestedAccess.value = false
+ }
}
}
diff --git a/packages/app/src/specs/SpecRunSummary.cy.tsx b/packages/app/src/specs/SpecRunSummary.cy.tsx
index 5f4f99b4e7e2..e711be4dd4a6 100644
--- a/packages/app/src/specs/SpecRunSummary.cy.tsx
+++ b/packages/app/src/specs/SpecRunSummary.cy.tsx
@@ -2,6 +2,40 @@ import SpecRunSummary from './SpecRunSummary.vue'
import { exampleRuns } from '@packages/frontend-shared/cypress/support/mock-graphql/fakeCloudSpecRun'
import type { CloudSpecRun } from '@packages/graphql/src/gen/cloud-source-types.gen'
+function validateTopBorder (color: string): void {
+ cy.findByTestId('spec-run-summary')
+ .should('have.css', 'border-top', `4px solid ${color}`)
+}
+
+function validateFilename (expected: string): void {
+ cy.findByTestId('spec-run-filename').should('have.text', expected)
+}
+
+function validateTimeAgo (expected: string): void {
+ cy.findByTestId('spec-run-time-ago')
+ .should('have.text', expected)
+}
+
+function validateStatus (status: string, color: string): void {
+ cy.findByTestId('spec-run-status')
+ .should('have.css', 'color', color)
+ .and('have.text', status)
+}
+
+function validateDuration1 (expected: string): void {
+ cy.findByTestId('spec-run-duration-1')
+ .should('have.text', expected)
+}
+
+function validateDuration2 (expected: string): void {
+ cy.findByTestId('spec-run-duration-2')
+ .should('have.text', expected)
+}
+
+function validateResultCountsVisible (): void {
+ cy.findByTestId('spec-run-result-counts').should('be.visible')
+}
+
describe('
', { keystrokeDelay: 0 }, () => {
const runs = exampleRuns()
@@ -19,27 +53,15 @@ describe('
', { keystrokeDelay: 0 }, () => {
})
it('should render expected content', () => {
- cy.findByTestId('spec-run-summary')
- // Green border at top
- .should('have.css', 'border-top', '4px solid rgb(31, 169, 113)')
-
- cy.findByTestId('spec-run-filename').should('have.text', 'mySpecFile.spec.ts')
-
- cy.findByTestId('spec-run-status')
- // Green text with expected status text
- .should('have.css', 'color', 'rgb(0, 129, 77)')
- .and('have.text', 'Passed')
-
- cy.findByTestId('spec-run-time-ago')
- .should('have.text', '1 year ago')
-
- cy.findByTestId('spec-run-duration-1')
- .should('have.text', '2:23')
-
- cy.findByTestId('spec-run-duration-2')
- .should('have.text', '2:39')
-
- cy.findByTestId('spec-run-result-counts').should('be.visible')
+ // Green border
+ validateTopBorder('rgb(31, 169, 113)')
+ validateFilename('mySpecFile.spec.ts')
+ // Green text
+ validateStatus('Passed', 'rgb(0, 129, 77)')
+ validateTimeAgo('1 year ago')
+ validateDuration1('2:23')
+ validateDuration2('2:39')
+ validateResultCountsVisible()
})
})
@@ -49,26 +71,15 @@ describe('
', { keystrokeDelay: 0 }, () => {
})
it('should render expected content', () => {
- cy.findByTestId('spec-run-summary')
- // Red border at top
- .should('have.css', 'border-top', '4px solid rgb(228, 87, 112)')
-
- cy.findByTestId('spec-run-filename').should('have.text', 'mySpecFile.spec.ts')
-
- cy.findByTestId('spec-run-status')
- // Red text with expected status text
- .should('have.css', 'color', 'rgb(198, 43, 73)')
- .and('have.text', 'Failed')
-
- cy.findByTestId('spec-run-time-ago')
- .should('have.text', '1 year ago')
-
- cy.findByTestId('spec-run-duration-1')
- .should('have.text', '1:02:40')
-
+ // Red border
+ validateTopBorder('rgb(228, 87, 112)')
+ validateFilename('mySpecFile.spec.ts')
+ // Red text
+ validateStatus('Failed', 'rgb(198, 43, 73)')
+ validateTimeAgo('1 year ago')
+ validateDuration1('1:02:40')
cy.findByTestId('spec-run-duration-2').should('not.exist')
-
- cy.findByTestId('spec-run-result-counts').should('be.visible')
+ validateResultCountsVisible()
})
})
@@ -78,27 +89,15 @@ describe('
', { keystrokeDelay: 0 }, () => {
})
it('should render expected content', () => {
- cy.findByTestId('spec-run-summary')
- // Gray border at top
- .should('have.css', 'border-top', '4px solid rgb(144, 149, 173)')
-
- cy.findByTestId('spec-run-filename').should('have.text', 'mySpecFile.spec.ts')
-
- cy.findByTestId('spec-run-status')
- // Gray text with expected status text
- .should('have.css', 'color', 'rgb(90, 95, 122)')
- .and('have.text', 'Canceled')
-
- cy.findByTestId('spec-run-time-ago')
- .should('have.text', '1 year ago')
-
- cy.findByTestId('spec-run-duration-1')
- .should('have.text', '2:23')
-
- cy.findByTestId('spec-run-duration-2')
- .should('have.text', '2:39')
-
- cy.findByTestId('spec-run-result-counts').should('be.visible')
+ // Gray border
+ validateTopBorder('rgb(144, 149, 173)')
+ validateFilename('mySpecFile.spec.ts')
+ // Gray text
+ validateStatus('Canceled', 'rgb(90, 95, 122)')
+ validateTimeAgo('1 year ago')
+ validateDuration1('2:23')
+ validateDuration2('2:39')
+ validateResultCountsVisible()
})
})
@@ -108,27 +107,15 @@ describe('
', { keystrokeDelay: 0 }, () => {
})
it('should render expected content', () => {
- cy.findByTestId('spec-run-summary')
- // Orange border at top
- .should('have.css', 'border-top', '4px solid rgb(219, 121, 3)')
-
- cy.findByTestId('spec-run-filename').should('have.text', 'mySpecFile.spec.ts')
-
- cy.findByTestId('spec-run-status')
- // Orange text with expected status text
- .should('have.css', 'color', 'rgb(189, 88, 0)')
- .and('have.text', 'Errored')
-
- cy.findByTestId('spec-run-time-ago')
- .should('have.text', '1 year ago')
-
- cy.findByTestId('spec-run-duration-1')
- .should('have.text', '1:02:40')
-
- cy.findByTestId('spec-run-duration-2')
- .should('have.text', '10:26:40')
-
- cy.findByTestId('spec-run-result-counts').should('be.visible')
+ // Orange border
+ validateTopBorder('rgb(219, 121, 3)')
+ validateFilename('mySpecFile.spec.ts')
+ // Orange text
+ validateStatus('Errored', 'rgb(189, 88, 0)')
+ validateTimeAgo('1 year ago')
+ validateDuration1('1:02:40')
+ validateDuration2('10:26:40')
+ validateResultCountsVisible()
})
})
@@ -138,117 +125,89 @@ describe('
', { keystrokeDelay: 0 }, () => {
})
it('should render expected content', () => {
- cy.findByTestId('spec-run-summary')
- // Gray border at top
- .should('have.css', 'border-top', '4px solid rgb(144, 149, 173)')
-
- cy.findByTestId('spec-run-filename').should('have.text', 'mySpecFile.spec.ts')
-
- cy.findByTestId('spec-run-status')
- // Gray text with expected status text
- .should('have.css', 'color', 'rgb(90, 95, 122)')
- .and('have.text', 'No tests')
-
- cy.findByTestId('spec-run-time-ago')
- .should('have.text', '1 year ago')
-
- cy.findByTestId('spec-run-duration-1')
- .should('have.text', '2:23')
-
- cy.findByTestId('spec-run-duration-2')
- .should('have.text', '2:39')
-
- cy.findByTestId('spec-run-result-counts').should('be.visible')
+ // Gray border
+ validateTopBorder('rgb(144, 149, 173)')
+ validateFilename('mySpecFile.spec.ts')
+ validateStatus('No tests', 'rgb(90, 95, 122)')
+ validateTimeAgo('1 year ago')
+ validateDuration1('2:23')
+ validateDuration2('2:39')
+ validateResultCountsVisible()
})
})
- context('over limit', () => {
+ context('running', () => {
beforeEach(() => {
- mountWithRun(runs[5])
+ mountWithRun(runs[6])
})
it('should render expected content', () => {
- cy.findByTestId('spec-run-summary')
- // Orange border at top
- .should('have.css', 'border-top', '4px solid rgb(219, 121, 3)')
-
- cy.findByTestId('spec-run-filename').should('have.text', 'mySpecFile.spec.ts')
-
- cy.findByTestId('spec-run-status')
- // Orange text with expected status text
- .should('have.css', 'color', 'rgb(189, 88, 0)')
- .and('have.text', 'Over limit')
-
- cy.findByTestId('spec-run-time-ago')
- .should('have.text', '1 year ago')
-
- cy.findByTestId('spec-run-duration-1')
- .should('have.text', '2:23')
-
- cy.findByTestId('spec-run-duration-2')
- .should('have.text', '2:39')
-
- cy.findByTestId('spec-run-result-counts').should('be.visible')
+ // Blue border
+ validateTopBorder('rgb(100, 112, 243)')
+ validateFilename('mySpecFile.spec.ts')
+ // Blue text
+ validateStatus('Running', 'rgb(73, 86, 227)')
+ validateTimeAgo('1 year ago')
+ validateDuration1('2:23')
+ validateDuration2('2:39')
+ validateResultCountsVisible()
})
})
- context('running', () => {
+ context('timed out', () => {
beforeEach(() => {
- mountWithRun(runs[6])
+ mountWithRun(runs[7])
})
it('should render expected content', () => {
- cy.findByTestId('spec-run-summary')
- // Blue border at top
- .should('have.css', 'border-top', '4px solid rgb(100, 112, 243)')
-
- cy.findByTestId('spec-run-filename').should('have.text', 'mySpecFile.spec.ts')
-
- cy.findByTestId('spec-run-status')
- // Blue text with expected status text
- .should('have.css', 'color', 'rgb(73, 86, 227)')
- .and('have.text', 'Running')
-
- cy.findByTestId('spec-run-time-ago')
- .should('have.text', '1 year ago')
-
- cy.findByTestId('spec-run-duration-1')
- .should('have.text', '2:23')
+ // Orange border
+ validateTopBorder('rgb(219, 121, 3)')
+ validateFilename('mySpecFile.spec.ts')
+ // Orange text
+ validateStatus('Timed out', 'rgb(189, 88, 0)')
+ validateTimeAgo('1 year ago')
+ validateDuration1('2:23')
+ validateDuration2('2:39')
+ validateResultCountsVisible()
+ })
+ })
- cy.findByTestId('spec-run-duration-2')
- .should('have.text', '2:39')
+ context('queued', () => {
+ beforeEach(() => {
+ mountWithRun(runs[8])
+ })
- cy.findByTestId('spec-run-result-counts').should('be.visible')
+ it('should render expected content', () => {
+ // Gray border
+ validateTopBorder('rgb(144, 149, 173)')
+ validateFilename('mySpecFile.spec.ts')
+ // Gray text
+ validateStatus('Queued', 'rgb(90, 95, 122)')
+ validateTimeAgo('1 year ago')
+ validateDuration1('2:23')
+ validateDuration2('2:39')
+ validateResultCountsVisible()
})
})
- context('timed out', () => {
+ context('unhandled status', () => {
beforeEach(() => {
- mountWithRun(runs[7])
+ mountWithRun(runs[9])
})
it('should render expected content', () => {
- cy.findByTestId('spec-run-summary')
- // Orange border at top
- .should('have.css', 'border-top', '4px solid rgb(219, 121, 3)')
-
- cy.findByTestId('spec-run-filename').should('have.text', 'mySpecFile.spec.ts')
+ // Gray border
+ validateTopBorder('rgb(144, 149, 173)')
+ validateFilename('mySpecFile.spec.ts')
+ // Should not render any status text
cy.findByTestId('spec-run-status')
- // Orange text with expected status text
- .should('have.css', 'color', 'rgb(189, 88, 0)')
- .and('have.text', 'Timed out')
-
- cy.findByTestId('spec-run-time-ago')
- .should('have.text', '1 year ago')
-
- cy.findByTestId('spec-run-duration-1')
- .should('have.text', '2:23')
-
- cy.findByTestId('spec-run-duration-2')
- .should('have.text', '2:39')
+ .should('not.exist')
- cy.findByTestId('spec-run-result-counts').should('be.visible')
+ validateTimeAgo('1 year ago')
+ validateDuration1('2:23')
+ validateDuration2('2:39')
+ validateResultCountsVisible()
})
})
})
diff --git a/packages/app/src/specs/SpecRunSummary.vue b/packages/app/src/specs/SpecRunSummary.vue
index 3b215bee0a2b..4ccee8ea1a53 100644
--- a/packages/app/src/specs/SpecRunSummary.vue
+++ b/packages/app/src/specs/SpecRunSummary.vue
@@ -119,8 +119,8 @@ const statusText = computed(() => {
case 'ERRORED': return 'Errored'
case 'FAILED': return 'Failed'
case 'NOTESTS': return 'No tests'
- case 'OVERLIMIT': return 'Over limit'
case 'PASSED': return 'Passed'
+ case 'UNCLAIMED': return 'Queued'
case 'RUNNING': return 'Running'
case 'TIMEDOUT': return 'Timed out'
default: return null
@@ -131,7 +131,6 @@ const statusColor = computed(() => {
if (!props.run?.status) return 'gray'
switch (props.run.status) {
- case 'OVERLIMIT':
case 'ERRORED':
case 'TIMEDOUT':
return 'orange'
@@ -143,6 +142,7 @@ const statusColor = computed(() => {
return 'indigo'
case 'CANCELLED':
case 'NOTESTS':
+ case 'UNCLAIMED':
default: return 'gray'
}
})
diff --git a/packages/app/src/specs/SpecsList.vue b/packages/app/src/specs/SpecsList.vue
index aa06ea61e6b0..515cb43ccbc0 100644
--- a/packages/app/src/specs/SpecsList.vue
+++ b/packages/app/src/specs/SpecsList.vue
@@ -184,7 +184,7 @@
class="h-full grid justify-items-end items-center"
>
{
+ const search = ref('')
+
+ cy.wrap(search).as('search')
+
+ cy.mount(defineComponent({
+ setup () {
+ return () => h(SpecsListHeader, {
+ modelValue: search.value,
+ 'onUpdate:modelValue': (val: string) => {
+ search.value = val
+ },
+ })
+ },
+ }))
+}
+
describe(' ', { keystrokeDelay: 0 }, () => {
it('can be searched', () => {
- const search = ref('')
const searchString = 'my/component.cy.tsx'
- cy.mount(defineComponent({
- setup () {
- return () => h(SpecsListHeader, {
- modelValue: search.value,
- 'onUpdate:modelValue': (val: string) => {
- search.value = val
- },
- })
- },
- }))
+ mountWithSearchRef()
cy.findByLabelText(defaultMessages.specPage.searchPlaceholder)
.type(searchString, { delay: 0 })
- .then(() => {
- expect(search.value).to.equal(searchString)
- })
+ .get('@search').its('value').should('eq', searchString)
+ })
+
+ it('clears search field when clear button is clicked', () => {
+ mountWithSearchRef()
+
+ cy.findByTestId('clear-search-button')
+ .should('not.exist')
+
+ cy.findByLabelText(defaultMessages.specPage.searchPlaceholder)
+ .type('abcd', { delay: 0 })
+ .get('@search').its('value').should('eq', 'abcd')
+
+ cy.findByTestId('clear-search-button')
+ .click()
+ .get('@search').its('value').should('eq', '')
})
it('emits a new spec event', () => {
diff --git a/packages/app/src/specs/SpecsListHeader.vue b/packages/app/src/specs/SpecsListHeader.vue
index 9af391d02ee0..bd791c939eb4 100644
--- a/packages/app/src/specs/SpecsListHeader.vue
+++ b/packages/app/src/specs/SpecsListHeader.vue
@@ -14,7 +14,17 @@
>
+
+
+
@@ -83,6 +93,10 @@ const onInput = (e: Event) => {
emit('update:modelValue', value)
}
+
+const clearInput = (e: Event) => {
+ emit('update:modelValue', '')
+}
-
+ Go to different origin:
+
+