UNPKG

@revoloo/cypress6

Version:

Cypress.io end to end testing tool

573 lines (467 loc) 16.4 kB
require('../../spec_helper') const _ = require('lodash') const debug = require('debug')('test') const commitInfo = require('@cypress/commit-info') const mockedEnv = require('mocked-env') const errors = require(`${root}../lib/errors`) const api = require(`${root}../lib/api`) const logger = require(`${root}../lib/logger`) const recordMode = require(`${root}../lib/modes/record`) const ciProvider = require(`${root}../lib/util/ci_provider`) const initialEnv = _.clone(process.env) // NOTE: the majority of the logic of record_spec is // tested as an e2e/record_spec describe('lib/modes/record', () => { // QUESTION: why are these tests here when // this is a module... ? context('.getCommitFromGitOrCi', () => { const gitCommit = { branch: null, } beforeEach(() => { delete process.env.CIRCLE_BRANCH delete process.env.TRAVIS_BRANCH delete process.env.BUILDKITE_BRANCH delete process.env.CI_BRANCH delete process.env.CIRCLECI delete process.env.TRAVIS delete process.env.BUILDKITE delete process.env.CI_NAME delete process.env.APPVEYOR delete process.env.APPVEYOR_REPO_BRANCH }) afterEach(() => { process.env = initialEnv }) it('gets branch from process.env.CIRCLE_BRANCH', () => { process.env.CIRCLECI = '1' process.env.CIRCLE_BRANCH = 'bem/circle' process.env.TRAVIS_BRANCH = 'bem/travis' process.env.CI_BRANCH = 'bem/ci' const commit = recordMode.getCommitFromGitOrCi(gitCommit) debug(commit) expect(commit.branch).to.eq('bem/circle') }) it('gets branch from process.env.TRAVIS_BRANCH', () => { process.env.TRAVIS = '1' process.env.TRAVIS_BRANCH = 'bem/travis' process.env.CI_BRANCH = 'bem/ci' const commit = recordMode.getCommitFromGitOrCi(gitCommit) debug(commit) expect(commit.branch).to.eq('bem/travis') }) it('gets branch from process.env.BUILDKITE_BRANCH', () => { process.env.BUILDKITE = '1' process.env.BUILDKITE_BRANCH = 'bem/buildkite' process.env.CI_BRANCH = 'bem/ci' const commit = recordMode.getCommitFromGitOrCi(gitCommit) debug(commit) expect(commit.branch).to.eq('bem/buildkite') }) it('gets branch from process.env.CI_BRANCH for codeship', () => { process.env.CI_NAME = 'codeship' process.env.CI_BRANCH = 'bem/ci' const commit = recordMode.getCommitFromGitOrCi(gitCommit) debug(commit) expect(commit.branch).to.eq('bem/ci') }) it('gets branch from process.env.APPVEYOR_REPO_BRANCH for AppVeyor', () => { process.env.APPVEYOR = '1' process.env.APPVEYOR_REPO_BRANCH = 'bem/app' const commit = recordMode.getCommitFromGitOrCi(gitCommit) debug(commit) expect(commit.branch).to.eq('bem/app') }) it('gets branch from git', () => { // this is tested inside @cypress/commit-info }) }) context('.createRunAndRecordSpecs', () => { describe('fallback commit information', () => { let resetEnv = null const env = { COMMIT_INFO_BRANCH: 'my-branch-221', COMMIT_INFO_MESSAGE: 'best commit ever', COMMIT_INFO_EMAIL: 'user@company.com', COMMIT_INFO_AUTHOR: 'Agent Smith', COMMIT_INFO_SHA: '0123456', COMMIT_INFO_REMOTE: 'remote repo', } beforeEach(() => { // stub git commands to return nulls sinon.stub(commitInfo, 'getBranch').resolves(null) sinon.stub(commitInfo, 'getMessage').resolves(null) sinon.stub(commitInfo, 'getEmail').resolves(null) sinon.stub(commitInfo, 'getAuthor').resolves(null) sinon.stub(commitInfo, 'getSha').resolves(null) sinon.stub(commitInfo, 'getRemoteOrigin').resolves(null) resetEnv = mockedEnv(env, { clear: true }) }) afterEach(() => { resetEnv() }) it('calls api.createRun with the commit extracted from environment variables', () => { const createRun = sinon.stub(api, 'createRun').resolves() const runAllSpecs = sinon.stub() return recordMode.createRunAndRecordSpecs({ key: 'foo', sys: {}, browser: {}, runAllSpecs, }) .then(() => { expect(runAllSpecs).to.have.been.calledWith({ parallel: false }) expect(createRun).to.have.been.calledOnce expect(createRun.firstCall.args).to.have.length(1) const { commit } = createRun.firstCall.args[0] debug('git is %o', commit) expect(commit).to.deep.equal({ sha: env.COMMIT_INFO_SHA, branch: env.COMMIT_INFO_BRANCH, authorName: env.COMMIT_INFO_AUTHOR, authorEmail: env.COMMIT_INFO_EMAIL, message: env.COMMIT_INFO_MESSAGE, remoteOrigin: env.COMMIT_INFO_REMOTE, defaultBranch: null, }) }) }) }) describe('override commit information', () => { let resetEnv = null const env = { COMMIT_INFO_BRANCH: 'my-branch-221', COMMIT_INFO_MESSAGE: 'best commit ever', COMMIT_INFO_EMAIL: 'user@company.com', COMMIT_INFO_AUTHOR: 'Agent Smith', COMMIT_INFO_SHA: '0123456', COMMIT_INFO_REMOTE: 'remote repo', } beforeEach(() => { // stub git commands to return values sinon.stub(commitInfo, 'getBranch').resolves('my-ci-branch') sinon.stub(commitInfo, 'getMessage').resolves('my ci msg') sinon.stub(commitInfo, 'getEmail').resolves('my.ci@email.com') sinon.stub(commitInfo, 'getAuthor').resolves('My CI Name') sinon.stub(commitInfo, 'getSha').resolves('mycisha') sinon.stub(commitInfo, 'getRemoteOrigin').resolves('my ci remote') resetEnv = mockedEnv(env, { clear: true }) }) afterEach(() => { resetEnv() }) it('calls api.createRun with the commit overrided from environment variables', () => { const createRun = sinon.stub(api, 'createRun').resolves() const runAllSpecs = sinon.stub() return recordMode.createRunAndRecordSpecs({ key: 'foo', sys: {}, browser: {}, runAllSpecs, }) .then(() => { expect(runAllSpecs).to.have.been.calledWith({ parallel: false }) expect(createRun).to.have.been.calledOnce expect(createRun.firstCall.args).to.have.length(1) const { commit } = createRun.firstCall.args[0] debug('git is %o', commit) expect(commit).to.deep.equal({ sha: env.COMMIT_INFO_SHA, branch: env.COMMIT_INFO_BRANCH, authorName: env.COMMIT_INFO_AUTHOR, authorEmail: env.COMMIT_INFO_EMAIL, message: env.COMMIT_INFO_MESSAGE, remoteOrigin: env.COMMIT_INFO_REMOTE, defaultBranch: null, }) }) }) }) describe('with CI info', () => { const specs = [ { relative: 'path/to/spec/a' }, { relative: 'path/to/spec/b' }, ] beforeEach(function () { sinon.stub(ciProvider, 'provider').returns('circle') sinon.stub(ciProvider, 'ciParams').returns({ foo: 'bar' }) this.commitDefaults = { branch: 'master', author: 'brian', email: 'brian@cypress.io', message: 'such hax', sha: 'sha-123', remote: 'https://github.com/foo/bar.git', } sinon.stub(commitInfo, 'commitInfo').resolves(this.commitDefaults) sinon.stub(ciProvider, 'commitDefaults').returns({ sha: this.commitDefaults.sha, branch: this.commitDefaults.branch, authorName: this.commitDefaults.author, authorEmail: this.commitDefaults.email, message: this.commitDefaults.message, remoteOrigin: this.commitDefaults.remote, }) sinon.stub(api, 'createRun').resolves() }) it('calls api.createRun with the right args', () => { const key = 'recordKey' const projectId = 'pId123' const specPattern = ['spec/pattern1', 'spec/pattern2'] const projectRoot = 'project/root' const ciBuildId = 'ciId123' const parallel = null const group = null const runAllSpecs = sinon.stub() const sys = { osCpus: 1, osName: 2, osMemory: 3, osVersion: 4, } const browser = { displayName: 'chrome', version: '59', } const tag = 'nightly,develop' return recordMode.createRunAndRecordSpecs({ key, sys, specs, group, browser, parallel, ciBuildId, projectId, projectRoot, specPattern, runAllSpecs, tag, }) .then(() => { expect(commitInfo.commitInfo).to.be.calledWith(projectRoot) expect(api.createRun).to.be.calledWith({ group, parallel, projectId, ciBuildId, recordKey: key, specPattern: 'spec/pattern1,spec/pattern2', specs: ['path/to/spec/a', 'path/to/spec/b'], platform: { osCpus: 1, osName: 2, osMemory: 3, osVersion: 4, browserName: 'chrome', browserVersion: '59', }, ci: { params: { foo: 'bar', }, provider: 'circle', }, commit: { authorEmail: 'brian@cypress.io', authorName: 'brian', branch: 'master', message: 'such hax', remoteOrigin: 'https://github.com/foo/bar.git', sha: 'sha-123', }, tags: ['nightly', 'develop'], }) }) }) }) }) context('.updateInstanceStdout', () => { beforeEach(function () { sinon.stub(api, 'updateInstanceStdout') this.options = { instanceId: 'id-123', captured: { toString () { return 'foobarbaz\n' }, }, } }) it('calls api.updateInstanceStdout', function () { api.updateInstanceStdout.resolves() return recordMode.updateInstanceStdout(this.options) .then(() => { expect(api.updateInstanceStdout).to.be.calledWith({ instanceId: 'id-123', stdout: 'foobarbaz\n', }) }) }) it('retries with backoff strategy', function () { sinon.stub(api, 'retryWithBackoff').yields().resolves() recordMode.updateInstanceStdout(this.options) expect(api.retryWithBackoff).to.be.called }) it('logs on retry', function () { sinon.stub(api, 'retryWithBackoff').yields().resolves() return recordMode.updateInstanceStdout(this.options) .then(() => { expect(api.retryWithBackoff).to.be.calledOnce }) }) it('does not createException when statusCode is 503', () => { const err = new Error('foo') err.statusCode = 503 sinon.spy(logger, 'createException') sinon.stub(api, 'retryWithBackoff').rejects(err) const options = { instanceId: 'id-123', captured: { toString () { return 'foobarbaz\n' } }, } return recordMode.updateInstanceStdout(options) .then(() => { expect(logger.createException).not.to.be.called }) }) }) context('.createInstance', () => { beforeEach(function () { sinon.stub(api, 'createInstance') this.options = { runId: 'run-123', groupId: 'group-123', machineId: 'machine-123', platform: {}, spec: { relative: 'cypress/integration/app_spec.coffee' }, } }) it('calls api.createInstance', function () { api.createInstance.resolves() return recordMode.createInstance(this.options) .then(() => { expect(api.createInstance).to.be.calledWith({ runId: 'run-123', groupId: 'group-123', machineId: 'machine-123', platform: {}, spec: 'cypress/integration/app_spec.coffee', }) }) }) it('retries with backoff strategy', function () { sinon.stub(api, 'retryWithBackoff').yields().resolves() recordMode.createInstance(this.options) expect(api.retryWithBackoff).to.be.called }) it('logs on retry', function () { sinon.stub(api, 'retryWithBackoff').yields().resolves() return recordMode.createInstance(this.options) .then(() => { expect(api.retryWithBackoff).to.be.calledOnce }) }) it('errors when statusCode is 503', async () => { const err = new Error('foo') err.statusCode = 503 sinon.spy(errors, 'get') sinon.stub(api, 'retryWithBackoff').rejects(err) await expect(recordMode.createInstance({ runId: 'run-123', groupId: 'group-123', machineId: 'machine-123', platform: {}, spec: { relative: 'cypress/integration/app_spec.coffee' }, })).to.be.rejected expect(errors.get).to.have.been.calledWith('DASHBOARD_CANNOT_PROCEED_IN_SERIAL') }) }) context('.createRun', () => { beforeEach(function () { sinon.stub(api, 'createRun') sinon.stub(ciProvider, 'ciParams').returns({}) sinon.stub(ciProvider, 'provider').returns('') sinon.stub(ciProvider, 'commitDefaults').returns({}) this.options = { git: {}, recordKey: '1', } }) it('retries with backoff strategy', function () { sinon.stub(api, 'retryWithBackoff').yields().resolves() return recordMode.createRun(this.options) .then(() => { expect(api.retryWithBackoff).to.be.called }) }) it('logs on retry', function () { sinon.stub(api, 'retryWithBackoff').yields().resolves() return recordMode.createRun(this.options) .then(() => { expect(api.retryWithBackoff).to.be.calledOnce }) }) // https://github.com/cypress-io/cypress/issues/14571 it('handles non-string key', async () => { const apiError = new Error('Invalid Record Key') apiError.statusCode = 401 sinon.stub(api, 'retryWithBackoff').rejects(apiError) sinon.spy(errors, 'throw') await expect(recordMode.createRun({ git: {}, recordKey: true, // instead of a string })).to.be.rejected expect(errors.throw).to.have.been.calledWith('DASHBOARD_RECORD_KEY_NOT_VALID', 'undefined') }) }) context('.postInstanceTests', () => { beforeEach(function () { sinon.stub(api, 'postInstanceTests') sinon.stub(ciProvider, 'ciParams').returns({}) sinon.stub(ciProvider, 'provider').returns('') sinon.stub(ciProvider, 'commitDefaults').returns({}) this.options = { results: {}, captured: '', } }) it('retries with backoff strategy', function () { sinon.stub(api, 'retryWithBackoff').yields().resolves() recordMode._postInstanceTests(this.options) expect(api.retryWithBackoff).to.be.called }) it('logs on retry', function () { sinon.stub(api, 'retryWithBackoff').yields().resolves() return recordMode._postInstanceTests(this.options) .then(() => { expect(api.retryWithBackoff).to.be.calledOnce }) }) }) context('.postInstanceResults', () => { beforeEach(function () { sinon.stub(api, 'postInstanceResults') sinon.stub(ciProvider, 'ciParams').returns({}) sinon.stub(ciProvider, 'provider').returns('') sinon.stub(ciProvider, 'commitDefaults').returns({}) this.options = { results: {}, captured: '', } }) it('retries with backoff strategy', function () { sinon.stub(api, 'retryWithBackoff').yields().resolves() recordMode.postInstanceResults(this.options) expect(api.retryWithBackoff).to.be.called }) it('logs on retry', function () { sinon.stub(api, 'retryWithBackoff').yields().resolves() return recordMode.postInstanceResults(this.options) .then(() => { expect(api.retryWithBackoff).to.be.calledOnce }) }) }) })