mirror of
https://github.com/cypress-io/cypress.git
synced 2026-04-22 07:00:22 -05:00
feat: add RecordRunModal component (#23953)
* feat: add RecordRunModal component * feat: add missing assertion * feat: import using @packages syntax * feat: remove unnecessary null check * feat: move record key gql logic to separate component * feat: add another test case * feat: refactor RunsEmpty component * feat: update test * feat: remove unused query * feat: use translation strings in tests * feat: assert that command shows in RecordRunModal Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com>
This commit is contained in:
@@ -20,7 +20,6 @@
|
||||
/>
|
||||
<RunsEmpty
|
||||
v-else-if="!currentProject?.cloudProject?.runs?.nodes.length"
|
||||
:gql="currentProject"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
@@ -80,7 +79,6 @@ fragment RunsContainer on Query {
|
||||
currentProject {
|
||||
id
|
||||
projectId
|
||||
...RunsEmpty
|
||||
...RunsConnectSuccessAlert
|
||||
cloudProject {
|
||||
__typename
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
import RunsEmpty from './RunsEmpty.vue'
|
||||
import { RunsEmptyFragmentDoc } from '../generated/graphql-test'
|
||||
import { defaultMessages } from '@cy/i18n'
|
||||
|
||||
describe('<RunsEmpty />', () => {
|
||||
it('playground', () => {
|
||||
cy.mountFragment(RunsEmptyFragmentDoc, {
|
||||
render (gqlVal) {
|
||||
return (<div class="h-screen">
|
||||
<RunsEmpty gql={gqlVal} />
|
||||
</div>)
|
||||
},
|
||||
})
|
||||
describe('RunsEmpty', () => {
|
||||
it('renders expected content', () => {
|
||||
cy.mount(<RunsEmpty />)
|
||||
|
||||
cy.gqlStub.Query.currentProject = {
|
||||
id: 'test_id',
|
||||
title: 'project_title',
|
||||
currentTestingType: 'component',
|
||||
cloudProject: {
|
||||
__typename: 'CloudProject',
|
||||
id: 'cloud_id',
|
||||
recordKeys: [{
|
||||
__typename: 'CloudRecordKey',
|
||||
id: 'recordKey1',
|
||||
key: 'abcd-efg-1234',
|
||||
}],
|
||||
} as any,
|
||||
} as any
|
||||
|
||||
cy.contains(defaultMessages.specPage.banners.record.title).should('be.visible')
|
||||
cy.contains('npx cypress run --component --record --key abcd-efg-1234').should('be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,59 +10,15 @@
|
||||
<p class="h-48px mb-8px text-gray-600">
|
||||
{{ t("runs.empty.description") }}
|
||||
</p>
|
||||
<TerminalPrompt
|
||||
:command="recordCommand"
|
||||
:project-folder-name="projectName"
|
||||
class="max-w-700px"
|
||||
/>
|
||||
<RecordPromptAdapter />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import { gql } from '@urql/vue'
|
||||
import TerminalPrompt from '@cy/components/TerminalPrompt.vue'
|
||||
import type { RunsEmptyFragment } from '../generated/graphql'
|
||||
import RecordPromptAdapter from '@packages/frontend-shared/src/gql-components/RecordPromptAdapter.vue'
|
||||
import { useI18n } from '@cy/i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
gql`
|
||||
fragment RunsEmpty on CurrentProject {
|
||||
id
|
||||
title
|
||||
projectId
|
||||
configFile
|
||||
currentTestingType
|
||||
cloudProject {
|
||||
__typename
|
||||
... on CloudProject {
|
||||
id
|
||||
recordKeys {
|
||||
id
|
||||
...RecordKey
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const props = defineProps<{
|
||||
gql: RunsEmptyFragment
|
||||
}>()
|
||||
|
||||
const projectName = computed(() => props.gql.title)
|
||||
const firstRecordKey = computed(() => {
|
||||
return props.gql.cloudProject?.__typename === 'CloudProject' && props.gql.cloudProject.recordKeys?.[0]?.key
|
||||
? props.gql.cloudProject.recordKeys[0].key
|
||||
: '<record-key>'
|
||||
})
|
||||
const recordCommand = computed(() => {
|
||||
const componentFlagOrSpace = props.gql.currentTestingType === 'component' ? ' --component ' : ' '
|
||||
|
||||
return `npx cypress run${componentFlagOrSpace}--record --key ${firstRecordKey.value}`
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -19,44 +19,16 @@
|
||||
<p class="mb-24px">
|
||||
{{ t('specPage.banners.record.content') }}
|
||||
</p>
|
||||
|
||||
<TerminalPrompt
|
||||
:command="recordCommand"
|
||||
:project-folder-name="query.data?.value?.currentProject?.title"
|
||||
class="bg-white max-w-900px"
|
||||
/>
|
||||
<RecordPromptAdapter />
|
||||
</TrackedBanner>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { gql, useQuery } from '@urql/vue'
|
||||
import RecordIcon from '~icons/cy/action-record_x16.svg'
|
||||
import { useI18n } from '@cy/i18n'
|
||||
import TerminalPrompt from '@cy/components/TerminalPrompt.vue'
|
||||
import TrackedBanner from './TrackedBanner.vue'
|
||||
import { BannerIds } from '@packages/types'
|
||||
import { RecordBannerDocument } from '../../generated/graphql'
|
||||
import { computed } from 'vue'
|
||||
|
||||
gql`
|
||||
query RecordBanner {
|
||||
currentProject {
|
||||
id
|
||||
title
|
||||
currentTestingType
|
||||
cloudProject {
|
||||
__typename
|
||||
... on CloudProject {
|
||||
id
|
||||
recordKeys {
|
||||
id
|
||||
key
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
import RecordPromptAdapter from '@packages/frontend-shared/src/gql-components/RecordPromptAdapter.vue'
|
||||
|
||||
defineProps<{
|
||||
modelValue: boolean
|
||||
@@ -70,16 +42,4 @@ const emit = defineEmits<{
|
||||
const { t } = useI18n()
|
||||
const bannerId = BannerIds.ACI_082022_RECORD
|
||||
|
||||
const query = useQuery({ query: RecordBannerDocument })
|
||||
|
||||
const firstRecordKey = computed(() => {
|
||||
return (query.data?.value?.currentProject?.cloudProject?.__typename === 'CloudProject' && query.data.value.currentProject.cloudProject.recordKeys?.[0]?.key) ?? '<record-key>'
|
||||
})
|
||||
|
||||
const recordCommand = computed(() => {
|
||||
const componentFlagOrSpace = query.data?.value?.currentProject?.currentTestingType === 'component' ? ' --component ' : ' '
|
||||
|
||||
return `npx cypress run${componentFlagOrSpace}--record --key ${firstRecordKey.value}`
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import RecordPromptVue from './RecordPrompt.vue'
|
||||
|
||||
describe('RecordPrompt', () => {
|
||||
it('renders with no props', () => {
|
||||
cy.mount(<RecordPromptVue />)
|
||||
|
||||
cy.findByText('npx cypress run --record --key <record-key>').should('be.visible')
|
||||
})
|
||||
|
||||
it('renders with component testing type', () => {
|
||||
cy.mount(<RecordPromptVue currentTestingType="component" />)
|
||||
|
||||
cy.findByText('npx cypress run --component --record --key <record-key>').should('be.visible')
|
||||
})
|
||||
|
||||
it('renders with record key', () => {
|
||||
cy.mount(<RecordPromptVue recordKey="abc-123" />)
|
||||
|
||||
cy.findByText('npx cypress run --record --key abc-123').should('be.visible')
|
||||
})
|
||||
|
||||
it('renders with record key for component testing', () => {
|
||||
cy.mount(<RecordPromptVue currentTestingType="component" recordKey="abc-123" />)
|
||||
|
||||
cy.findByText('npx cypress run --component --record --key abc-123').should('be.visible')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<TerminalPrompt
|
||||
:command="recordCommand"
|
||||
class="bg-white max-w-900px"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import TerminalPrompt from '@cy/components/TerminalPrompt.vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
recordKey?: string | null
|
||||
currentTestingType?: string | null
|
||||
}>()
|
||||
|
||||
const firstRecordKey = computed(() => {
|
||||
return props.recordKey ?? '<record-key>'
|
||||
})
|
||||
|
||||
const recordCommand = computed(() => {
|
||||
const componentFlagOrSpace = props.currentTestingType === 'component' ? ' --component ' : ' '
|
||||
|
||||
return `npx cypress run${componentFlagOrSpace}--record --key ${firstRecordKey.value}`
|
||||
})
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<RecordPrompt
|
||||
:record-key="query.data.value?.currentProject?.cloudProject?.__typename === 'CloudProject' ? query.data.value?.currentProject.cloudProject.recordKeys?.[0]?.key : ''"
|
||||
:current-testing-type="query.data.value?.currentProject?.currentTestingType"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { gql, useQuery } from '@urql/vue'
|
||||
import { RecordPromptAdapterDocument } from '../generated/graphql'
|
||||
import RecordPrompt from './RecordPrompt.vue'
|
||||
|
||||
gql`
|
||||
query RecordPromptAdapter {
|
||||
currentProject {
|
||||
id
|
||||
title
|
||||
currentTestingType
|
||||
cloudProject {
|
||||
__typename
|
||||
... on CloudProject {
|
||||
id
|
||||
recordKeys {
|
||||
id
|
||||
key
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const query = useQuery({ query: RecordPromptAdapterDocument })
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,43 @@
|
||||
import RecordRunModalVue from './RecordRunModal.vue'
|
||||
import { defaultMessages } from '@cy/i18n'
|
||||
|
||||
describe('RecordRunModal', () => {
|
||||
it('is not open by default', () => {
|
||||
cy.mount(<RecordRunModalVue isModalOpen={false} close={cy.stub()} utmMedium="Nav" />)
|
||||
|
||||
cy.findByTestId('record-run-modal').should('not.exist')
|
||||
})
|
||||
|
||||
it('renders open', () => {
|
||||
cy.mount(<RecordRunModalVue isModalOpen={true} close={cy.stub()} utmMedium="Nav" />)
|
||||
|
||||
cy.contains(defaultMessages.specPage.banners.record.title).should('be.visible')
|
||||
cy.findByTestId('copy-button').should('be.visible')
|
||||
|
||||
cy.contains('npx cypress run --record --key 2aaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa').should('be.visible')
|
||||
|
||||
cy.percySnapshot()
|
||||
})
|
||||
|
||||
it('calls close when X is clicked', () => {
|
||||
const closeStub = cy.stub()
|
||||
|
||||
cy.mount(<RecordRunModalVue isModalOpen={true} close={closeStub} utmMedium="Nav" />)
|
||||
|
||||
cy.findByRole('button', { name: defaultMessages.actions.close }).click().then(() => {
|
||||
expect(closeStub).to.have.been.called
|
||||
})
|
||||
})
|
||||
|
||||
it('sends UTM parameters with help link', () => {
|
||||
cy.mount(<RecordRunModalVue isModalOpen={true} close={cy.stub()} utmMedium="Nav" utmContent="content" />)
|
||||
|
||||
cy.contains(defaultMessages.links.needHelp).should('have.attr', 'href', 'https://on.cypress.io/cypress-run-record-key?utm_medium=Nav&utm_source=Binary%3A+Launchpad&utm_content=content')
|
||||
})
|
||||
|
||||
it('sends UTM parameters with help link without UTM content prop', () => {
|
||||
cy.mount(<RecordRunModalVue isModalOpen={true} close={cy.stub()} utmMedium="Nav" />)
|
||||
|
||||
cy.contains(defaultMessages.links.needHelp).should('have.attr', 'href', 'https://on.cypress.io/cypress-run-record-key?utm_medium=Nav&utm_source=Binary%3A+Launchpad&utm_content=')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<StandardModal
|
||||
class="transition transition-all duration-200"
|
||||
variant="bare"
|
||||
:title="t('specPage.banners.record.title')"
|
||||
:model-value="isModalOpen"
|
||||
:help-link="helpLink"
|
||||
:no-help="!helpLink"
|
||||
data-cy="record-run-modal"
|
||||
@update:model-value="close"
|
||||
>
|
||||
<div class="max-w-175 py-7 px-6 text-gray-600">
|
||||
<p class="mb-24px">
|
||||
{{ t('specPage.banners.record.content') }}
|
||||
</p>
|
||||
<RecordPromptAdapter />
|
||||
</div>
|
||||
</StandardModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" >
|
||||
import { useI18n } from '@cy/i18n'
|
||||
import StandardModal from '../components/StandardModal.vue'
|
||||
import RecordPromptAdapter from './RecordPromptAdapter.vue'
|
||||
import { getUtmSource } from '../utils/getUtmSource'
|
||||
import { getUrlWithParams } from '../utils/getUrlWithParams'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps<{
|
||||
isModalOpen: boolean
|
||||
close: () => void
|
||||
utmMedium: string
|
||||
utmContent?: string
|
||||
}>()
|
||||
|
||||
const helpLink = getUrlWithParams({
|
||||
url: 'https://on.cypress.io/cypress-run-record-key',
|
||||
params: {
|
||||
utm_medium: props.utmMedium,
|
||||
utm_source: getUtmSource(),
|
||||
utm_content: props.utmContent || '',
|
||||
},
|
||||
})
|
||||
|
||||
</script>
|
||||
Reference in New Issue
Block a user