Merge branch '10.0-release' into md-10.0-merge

This commit is contained in:
Bill Glesias
2022-05-09 16:06:20 -04:00
committed by GitHub
16 changed files with 148 additions and 55 deletions

2
.vscode/cspell.json vendored
View File

@@ -16,6 +16,7 @@
"Pinia",
"pnpm",
"pseudoclass",
"revparse",
"Screenshotting",
"shiki",
"testid",
@@ -23,6 +24,7 @@
"unconfigured",
"unplugin",
"unrunnable",
"unstaged",
"urql",
"vite",
"vitejs",

View File

@@ -25,6 +25,9 @@ import { setActivePinia } from 'pinia'
import type { Pinia } from 'pinia'
import 'cypress-real-events/support'
import { installCustomPercyCommand } from '@packages/ui-components/cypress/support/customPercyCommand'
import { configure } from '@testing-library/cypress'
configure({ testIdAttribute: 'data-cy' })
let pinia: Pinia

View File

@@ -29,7 +29,7 @@ describe('Cypress In Cypress CT', { viewportWidth: 1500, defaultCommandTimeout:
snapshotAUTPanel('browsers open')
cy.contains('Canary').should('be.hidden')
cy.contains('The viewport determines the width and height of your application. By default the viewport will be 500px by 500px for Component Testing unless specified by a cy.viewport command.')
cy.contains('The viewport determines the width and height of your application under test. By default the viewport will be 500px by 500px for component testing.')
.should('be.visible')
snapshotAUTPanel('viewport info open')

View File

@@ -35,7 +35,7 @@ describe('Cypress In Cypress E2E', { viewportWidth: 1500, defaultCommandTimeout:
cy.contains('Canary').should('be.hidden')
cy.get('[data-cy="viewport"]').click()
cy.contains('The viewport determines the width and height of your application. By default the viewport will be 1000px by 660px for End-to-end Testing unless specified by a cy.viewport command.')
cy.contains('The viewport determines the width and height of your application under test. By default the viewport will be 1000px by 660px for end-to-end testing.')
.should('be.visible')
snapshotAUTPanel('viewport info open')

View File

@@ -28,16 +28,16 @@ describe('Reporter Header', () => {
cy.get('input').type('dom', { force: true })
cy.get('[data-testid="spec-file-item"]').should('have.length', 3)
cy.get('[data-cy="spec-file-item"]').should('have.length', 3)
.should('contain', 'dom-content.spec')
cy.get('input').clear()
cy.get('[data-testid="spec-file-item"]').should('have.length', '3')
cy.get('[data-cy="spec-file-item"]').should('have.length', '3')
cy.get('input').type('asdf', { force: true })
cy.get('[data-testid="spec-file-item"]').should('have.length', 0)
cy.get('[data-cy="spec-file-item"]').should('have.length', 0)
})
})

View File

@@ -567,10 +567,10 @@ describe('App: Specs', () => {
cy.log('should not contain the link if you navigate away and back')
cy.get('body').type('f')
cy.get('[data-testid=spec-file-item]').first().click()
cy.get('[data-cy=spec-file-item]').first().click()
cy.get('#spec-runner-header').should('not.contain', 'Review the docs')
cy.get('[data-testid=spec-file-item]').last().click()
cy.get('[data-cy=spec-file-item]').last().click()
cy.get('#spec-runner-header').should('not.contain', 'Review the docs')
})
})

View File

@@ -40,5 +40,36 @@ describe('Spec List - Git Status', () => {
cy.get('[data-cy-row="dom-container.spec.js"]')
.contains('Modified')
.get('[data-cy="git-status-modified"]')
cy.withCtx((ctx) => {
ctx.fs.appendFileSync(
ctx.path.join(ctx.currentProject!, 'cypress', 'e2e', 'foo.spec.js'),
'// modifying the spec.',
'utf-8',
)
})
// even if a created file is updated, the status should stay created
cy.get('[data-cy-row="foo.spec.js"]')
.contains('Created')
.get('[data-cy="git-status-created"]')
cy.withCtx((ctx) => {
ctx.fs.writeFileSync(
ctx.path.join(ctx.currentProject!, 'cypress', 'e2e', 'dom-container.spec.js'),
`describe('Dom Content', () => {
it('renders a container', () => {
cy.get('.container')
})
})
`,
'utf-8',
)
})
// reverting the updates to a file before committing should revert its status
cy.get('[data-cy-row="dom-container.spec.js"]')
.contains('Modified')
.get('[data-cy="git-status-unmodified"]')
})
})

View File

@@ -148,7 +148,7 @@ module.exports = {
cy.get('[data-model-state="passed"]').should('contain', 'renders the test content')
cy.get('body').type('f')
cy.get('[data-testid="spec-file-item"]')
cy.get('[data-cy="spec-file-item"]')
.should('have.length', 6)
.should('contain', 'blank-contents.spec.js')
.should('contain', 'dom-container.spec.js')
@@ -159,7 +159,7 @@ module.exports = {
await ctx.actions.file.writeFileInProject(o.path, '')
}, { path: getPathForPlatform('cypress/e2e/new-file.spec.js') })
cy.get('[data-testid="spec-file-item"]')
cy.get('[data-cy="spec-file-item"]')
.should('have.length', 7)
.should('contain', 'blank-contents.spec.js')
.should('contain', 'dom-container.spec.js')
@@ -173,7 +173,7 @@ module.exports = {
cy.get('[data-model-state="passed"]').should('contain', 'renders the test content')
cy.get('body').type('f')
cy.get('[data-testid="spec-file-item"]')
cy.get('[data-cy="spec-file-item"]')
.should('have.length', 6)
.should('contain', 'blank-contents.spec.js')
.should('contain', 'dom-container.spec.js')
@@ -184,7 +184,7 @@ module.exports = {
await ctx.actions.file.removeFileInProject(o.path)
}, { path: getPathForPlatform('cypress/e2e/dom-list.spec.js') })
cy.get('[data-testid="spec-file-item"]')
cy.get('[data-cy="spec-file-item"]')
.should('have.length', 5)
.should('contain', 'blank-contents.spec.js')
.should('contain', 'dom-container.spec.js')
@@ -207,7 +207,7 @@ module.exports = {
],
})
cy.get('[data-testid="spec-file-item"]')
cy.get('[data-cy="spec-file-item"]')
.should('have.length', 1)
.should('contain', 'dom-content.spec.js')
@@ -224,7 +224,7 @@ module.exports = {
cy.get('[data-model-state="passed"]').should('contain', 'renders the test content')
cy.get('body').type('f')
cy.get('[data-testid="spec-file-item"]')
cy.get('[data-cy="spec-file-item"]')
.should('have.length', 6)
.should('contain', 'blank-contents.spec.js')
.should('contain', 'dom-container.spec.js')
@@ -253,7 +253,7 @@ module.exports = {
}`)
})
cy.get('[data-testid="spec-file-item"]', { timeout: 7500 })
cy.get('[data-cy="spec-file-item"]', { timeout: 7500 })
.should('have.length', 2)
.should('contain', 'dom-container.spec.js')
.should('contain', 'dom-content.spec.js')

View File

@@ -8,7 +8,10 @@ function renderWithGql (gqlVal: SpecRunnerHeaderFragment) {
const autIframe = createTestAutIframe()
return (<SpecRunnerHeaderOpenMode
gql={gqlVal}
gql={{
...gqlVal,
configFile: gqlVal.configFile || 'cypress.config.ts',
}}
eventManager={eventManager}
getAutIframe={() => autIframe}/>)
}
@@ -39,7 +42,6 @@ describe('SpecRunnerHeaderOpenMode', { viewportHeight: 500 }, () => {
})
cy.get('[data-cy="playground-activator"]').should('be.disabled')
cy.percySnapshot()
})
@@ -66,6 +68,7 @@ describe('SpecRunnerHeaderOpenMode', { viewportHeight: 500 }, () => {
})
cy.get('[data-cy="playground-activator"]').should('not.be.disabled')
cy.percySnapshot()
})
it('shows url section if currentTestingType is e2e', () => {
@@ -83,6 +86,7 @@ describe('SpecRunnerHeaderOpenMode', { viewportHeight: 500 }, () => {
})
cy.get('[data-cy="aut-url"]').should('exist')
cy.percySnapshot()
})
it('url section handles long url/small viewport', {
@@ -121,6 +125,7 @@ describe('SpecRunnerHeaderOpenMode', { viewportHeight: 500 }, () => {
cy.get('[data-cy="playground-activator"]').should('be.visible')
cy.get('[data-cy="aut-url"]').should('not.exist')
cy.percySnapshot()
})
it('shows current browser and possible browsers', () => {
@@ -139,17 +144,24 @@ describe('SpecRunnerHeaderOpenMode', { viewportHeight: 500 }, () => {
cy.get('[data-cy="select-browser"] button[aria-controls]').focus().type('{enter}')
cy.contains('Firefox').should('be.hidden')
cy.percySnapshot()
})
it('shows current viewport info', () => {
cy.mountFragment(SpecRunnerHeaderFragmentDoc, {
render: (gqlVal) => {
return renderWithGql(gqlVal)
return renderWithGql({
...gqlVal,
configFile: 'cypress.config.js',
})
},
})
cy.get('[data-cy="viewport"]').click()
cy.contains('The viewport determines').should('be.visible')
cy.contains('Additionally, you can override this value in your cypress.config.js or via the cy.viewport() command.')
.should('be.visible')
cy.get('[data-cy="viewport"]').click()
cy.contains('The viewport determines').should('be.hidden')
cy.get('[data-cy="viewport"] button').focus().type(' ')
@@ -158,6 +170,27 @@ describe('SpecRunnerHeaderOpenMode', { viewportHeight: 500 }, () => {
cy.contains('The viewport determines').should('be.hidden')
})
it('links to the viewport docs', () => {
cy.mountFragment(SpecRunnerHeaderFragmentDoc, {
render: (gqlVal) => {
return renderWithGql({
...gqlVal,
currentTestingType: 'e2e',
})
},
})
cy.findByTestId('viewport').click()
cy.findByTestId('viewport-docs')
.should('be.visible')
.should('have.attr', 'href', 'https://on.cypress.io/viewport')
cy.contains('Additionally, you can override this value in your cypress.config.ts or via the cy.viewport() command.')
.should('be.visible')
cy.percySnapshot()
})
it('disables browser dropdown button when isRunning is true', () => {
const autStore = useAutStore()
@@ -173,7 +206,6 @@ describe('SpecRunnerHeaderOpenMode', { viewportHeight: 500 }, () => {
})
cy.get('[data-cy="select-browser"] > button').should('be.disabled')
cy.percySnapshot()
})
})

View File

@@ -77,30 +77,40 @@
</span>
</template>
<template #default>
<div class="max-h-50vw p-16px text-gray-600 leading-24px w-400px overflow-auto">
<!-- TODO: UNIFY-1316 - This copy is a placeholder based on the old message for this, we should confirm the exact copy and then move to i18n -->
<p class="mb-16px">
The
<strong>viewport</strong> determines the width and height of your application.
By default the viewport will be
<strong>{{ autStore.defaultViewportWidth }}px</strong> by
<strong>{{ autStore.defaultViewportHeight }}px</strong> for {{ props.gql.currentTestingType === "e2e" ? 'End-to-end' : 'Component' }}
Testing unless specified by a <InlineCodeFragment>cy.viewport</InlineCodeFragment> command.
</p>
<p class="mb-16px">
Additionally, you can override the default viewport dimensions by specifying these values in your config file:
</p>
<div class="max-h-50vw p-24px pt-5 text-gray-700 leading-5 w-346px overflow-auto">
<i18n-t
tag="p"
keypath="runner.viewportTooltip.infoText"
class="mb-24px"
>
<strong class="font-bold">{{ autStore.defaultViewportWidth }}px</strong>
<strong class="font-bold">{{ autStore.defaultViewportHeight }}px</strong>
{{ props.gql.currentTestingType === "e2e" ? 'end-to-end' : 'component' }}
</i18n-t>
<ShikiHighlight
class="rounded border-gray-200 border-1 mb-16px"
lang="javascript"
:code="code"
/>
<p>
<ExternalLink href="https://on.cypress.io/viewport">
Read more about viewport here.
</ExternalLink>
</p>
<i18n-t
tag="p"
keypath="runner.viewportTooltip.configText"
class="mb-24px"
>
<!-- disable rule to prevent trailing space from being added to <InlineCodeFragment/> -->
<!-- eslint-disable-next-line vue/singleline-html-element-content-newline -->
<InlineCodeFragment class="text-xs font-medium leading-5">{{ props.gql.configFile }}</InlineCodeFragment>
<!-- eslint-disable-next-line vue/singleline-html-element-content-newline -->
<InlineCodeFragment class="text-xs font-medium leading-5">cy.viewport()</InlineCodeFragment>
</i18n-t>
<div class="flex justify-center">
<Button
class="font-medium"
data-cy="viewport-docs"
:prefix-icon="BookIcon"
prefix-icon-class="icon-dark-indigo-500"
variant="outline"
:href="t('runner.viewportTooltip.buttonHref')"
>
{{ t('runner.viewportTooltip.buttonText') }}
</Button>
</div>
</div>
</template>
</SpecRunnerDropdown>
@@ -143,17 +153,17 @@ import SelectorPlayground from './selector-playground/SelectorPlayground.vue'
import ExternalLink from '@packages/frontend-shared/src/gql-components/ExternalLink.vue'
import Alert from '@packages/frontend-shared/src/components/Alert.vue'
import Button from '@packages/frontend-shared/src/components/Button.vue'
import ShikiHighlight from '@packages/frontend-shared/src/components/ShikiHighlight.vue'
import VerticalBrowserListItems from '@packages/frontend-shared/src/gql-components/topnav/VerticalBrowserListItems.vue'
import InlineCodeFragment from '@packages/frontend-shared/src/components/InlineCodeFragment.vue'
import SpecRunnerDropdown from './SpecRunnerDropdown.vue'
import { allBrowsersIcons } from '@packages/frontend-shared/src/assets/browserLogos'
import BookIcon from '~icons/cy/book_x16'
gql`
fragment SpecRunnerHeader on CurrentProject {
id
configFile
currentTestingType
activeBrowser {
id
displayName
@@ -203,8 +213,4 @@ const activeSpecPath = specStore.activeSpec?.absolute
const isDisabled = computed(() => autStore.isRunning || autStore.isLoading)
const code = `{
"viewportWidth": ${autStore.defaultViewportWidth},
"viewportHeight": ${autStore.defaultViewportHeight}
}`
</script>

View File

@@ -15,7 +15,7 @@
flex
group
relative"
data-testid="spec-row-item"
data-cy="spec-row-item"
:data-selected-spec="isCurrentSpec(row.data)"
@click.self="submit(row.data, row.index)"
>
@@ -42,7 +42,7 @@
:selected="isCurrentSpec(row.data)"
:indexes="row.data?.data?.fileIndexes"
class="pl-22px"
data-testid="spec-file-item"
data-cy="spec-file-item"
/>
<DirectoryItem
v-else
@@ -50,7 +50,7 @@
:name="row.data.name"
:expanded="treeSpecList[row.index].expanded.value"
:indexes="getDirIndexes(row.data)"
data-testid="directory-item"
data-cy="directory-item"
/>
</RouterLink>
</li>

View File

@@ -302,7 +302,7 @@ export class ProjectActions {
setSpecs (specs: FoundSpec[]) {
this.ctx.project.setSpecs(specs)
this.ctx.lifecycleManager.git?.setSpecs(specs.map((s) => s.absolute))
this.refreshSpecs(specs)
if (this.ctx.coreData.currentTestingType === 'component') {
this.api.getDevServer().updateSpecs(specs)
@@ -311,6 +311,10 @@ export class ProjectActions {
this.ctx.emitter.specsChange()
}
refreshSpecs (specs: FoundSpec[]) {
this.ctx.lifecycleManager.git?.setSpecs(specs.map((s) => s.absolute))
}
async setProjectPreferences (args: MutationSetProjectPreferencesArgs) {
if (!this.ctx.currentProject) {
throw Error(`Cannot save preferences without currentProject.`)

View File

@@ -31,7 +31,7 @@ dayjs.extend(relativeTime)
// $ git log -1 --pretty=format:%ci %ar %an <file>
// eg '2021-09-14 13:43:19 +1000 2 days ago Lachlan Miller
const GIT_LOG_REGEXP = /(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-+].+?)\s(.+ago)\s(.*)/
const GIT_LOG_COMMAND = `git log -1 --pretty="format:%ci %ar %an"`
const GIT_LOG_COMMAND = `git log --max-count=1 --pretty="format:%ci %ar %an"`
const GIT_ROOT_DIR_COMMAND = '--show-toplevel'
const SIXTY_SECONDS = 60 * 1000
@@ -83,8 +83,10 @@ export class GitDataSource {
// Simple Git will error if the projectRoot does not exist.
// This should never happen outside of testing code simulating
// incorrect scenarios
debug('config: %o', this.config)
try {
this.#git = simpleGit({ baseDir: this.config.projectRoot })
this.#gitBaseDir = this.config.projectRoot
} catch {
this.#git = simpleGit()
}
@@ -124,6 +126,8 @@ export class GitDataSource {
}
this.#loadBulkGitInfo(specs).catch(this.config.onError)
this.#specs = specs
}
get gitBaseDir () {
@@ -301,10 +305,15 @@ export class GitDataSource {
: `IFS=$'\n'; for file in {${paths}}; do echo $(${GIT_LOG_COMMAND} $file); done`
debug('executing command `%s`:', cmd)
debug('gitBaseDir `%s`:', this.#gitBaseDir)
const result = await execa(cmd, { shell: process.env.SHELL || '/bin/bash' })
const result = await execa(cmd, { shell: process.env.SHELL || '/bin/bash', cwd: this.#gitBaseDir })
const stdout = result.stdout.split('\n')
if (result.exitCode !== 0) {
debug(`error... stderr`, result.stderr)
}
if (stdout.length !== absolutePaths.length) {
debug('error... stdout:', stdout)
throw Error(`Expect result array to have same length as input. Input: ${absolutePaths.length} Output: ${stdout.length}`)

View File

@@ -259,6 +259,8 @@ export class ProjectDataSource {
const specs = await this.findSpecs(projectRoot, testingType, specPattern, excludeSpecPattern, additionalIgnore)
if (isEqual(this.specs, specs)) {
this.ctx.actions.project.refreshSpecs(specs)
// If no differences are found, we do not need to emit events
return
}

View File

@@ -61,8 +61,6 @@ describe('GitDataSource', () => {
fs.createFileSync(fooSpec)
fs.writeFileSync(xhrSpec, 'it(\'modifies the file\', () => {})')
process.chdir(projectPath)
const dfd = pDefer()
onGitInfoChange.onFirstCall().callsFake(dfd.resolve)

View File

@@ -678,6 +678,12 @@
"shared": {
"link": "Read more about browser management"
}
},
"viewportTooltip": {
"buttonText": "Review the guide on viewports",
"buttonHref": "https://on.cypress.io/viewport",
"infoText": "The viewport determines the width and height of your application under test. By default the viewport will be {0} by {1} for {2} testing.",
"configText": "Additionally, you can override this value in your {2} or via the {4} command."
}
},
"warnings": {