From 52d7316038604c4f22d18d2114402f7784c89813 Mon Sep 17 00:00:00 2001 From: KHeo Date: Tue, 15 Feb 2022 10:28:27 +0900 Subject: [PATCH 1/8] chore: move tests to its own file. --- .../commands/actions/type_events_spec.js | 770 ++++++++++++++++++ .../integration/commands/actions/type_spec.js | 761 ----------------- 2 files changed, 770 insertions(+), 761 deletions(-) create mode 100644 packages/driver/cypress/integration/commands/actions/type_events_spec.js diff --git a/packages/driver/cypress/integration/commands/actions/type_events_spec.js b/packages/driver/cypress/integration/commands/actions/type_events_spec.js new file mode 100644 index 0000000000..697c1dd670 --- /dev/null +++ b/packages/driver/cypress/integration/commands/actions/type_events_spec.js @@ -0,0 +1,770 @@ +const { _, $ } = Cypress + +describe('src/cy/commands/actions/type - #type events', () => { + beforeEach(() => { + cy.visit('fixtures/dom.html') + }) + + describe('events', () => { + it('receives keydown event', (done) => { + const $txt = cy.$$(':text:first') + + $txt.on('keydown', (e) => { + expect(_.toPlainObject(e.originalEvent)).to.include({ + altKey: false, + bubbles: true, + cancelable: true, + charCode: 0, // deprecated + ctrlKey: false, + detail: 0, + key: 'a', + // has code property https://github.com/cypress-io/cypress/issues/3722 + code: 'KeyA', + keyCode: 65, // deprecated but fired by chrome always uppercase in the ASCII table + location: 0, + metaKey: false, + repeat: false, + shiftKey: false, + type: 'keydown', + which: 65, // deprecated but fired by chrome + }) + + done() + }) + + cy.get(':text:first').type('a') + }) + + it('receives keypress event', (done) => { + const $txt = cy.$$(':text:first') + + $txt.on('keypress', (e) => { + expect(_.toPlainObject(e.originalEvent)).to.include({ + altKey: false, + bubbles: true, + cancelable: true, + charCode: 97, // deprecated + ctrlKey: false, + detail: 0, + key: 'a', + code: 'KeyA', + keyCode: 97, // deprecated + location: 0, + metaKey: false, + repeat: false, + shiftKey: false, + type: 'keypress', + which: 97, // deprecated + }) + + done() + }) + + cy.get(':text:first').type('a') + }) + + it('receives keyup event', (done) => { + const $txt = cy.$$(':text:first') + + $txt.on('keyup', (e) => { + expect(_.toPlainObject(e.originalEvent)).to.include({ + altKey: false, + bubbles: true, + cancelable: true, + charCode: 0, // deprecated + ctrlKey: false, + detail: 0, + key: 'a', + code: 'KeyA', + keyCode: 65, // deprecated but fired by chrome always uppercase in the ASCII table + location: 0, + metaKey: false, + repeat: false, + shiftKey: false, + type: 'keyup', + view: cy.state('window'), + which: 65, // deprecated but fired by chrome + }) + .not.have.property('inputType') + + done() + }) + + cy.get(':text:first').type('a') + }) + + it('receives textInput event', (done) => { + const $txt = cy.$$(':text:first') + + $txt[0].addEventListener('textInput', (e) => { + // FIXME: (firefox) firefox cannot access window objects else throw cross-origin error + expect(Object.prototype.toString.call(e.view)).eq('[object Window]') + e.view = null + expect(_.toPlainObject(e)).to.include({ + bubbles: true, + cancelable: true, + data: 'a', + detail: 0, + type: 'textInput', + // view: cy.state('window'), + which: 0, + }) + + done() + }) + + cy.get(':text:first').type('a') + }) + + it('receives input event', (done) => { + const $txt = cy.$$(':text:first') + + $txt.on('input', (e) => { + const obj = _.pick(e.originalEvent, 'bubbles', 'cancelable', 'type') + + expect(obj).to.deep.eq({ + bubbles: true, + cancelable: false, + type: 'input', + }) + + done() + }) + + cy.get(':text:first').type('a') + }) + + it('fires events in the correct order') + + it('fires events for each key stroke') + + it('does fire input event when value changes', () => { + const onInput = cy.stub() + + cy.$$(':text:first').on('input', onInput) + + cy.get(':text:first') + .invoke('val', 'bar') + .type('{selectAll}{rightarrow}{backspace}') + .then(() => { + expect(onInput).to.be.calledOnce + }) + .then(() => { + onInput.resetHistory() + }) + + cy.get(':text:first') + .invoke('val', 'bar') + .type('{selectAll}{leftarrow}{del}') + .then(() => { + expect(onInput).to.be.calledOnce + }) + .then(() => { + onInput.resetHistory() + }) + + cy.$$('[contenteditable]:first').on('input', onInput) + + cy.get('[contenteditable]:first') + .invoke('html', 'foobar') + .type('{selectAll}{rightarrow}{backspace}') + .then(() => { + expect(onInput).to.be.calledOnce + }) + .then(() => { + onInput.resetHistory() + }) + + cy.get('[contenteditable]:first') + .invoke('html', 'foobar') + .type('{selectAll}{leftarrow}{del}') + .then(() => { + expect(onInput).to.be.calledOnce + }) + }) + + it('does not fire input event when value does not change', () => { + let fired = false + + cy.$$(':text:first').on('input', () => { + fired = true + }) + + cy.get(':text:first') + .invoke('val', 'bar') + .type('{selectAll}{rightarrow}{del}') + .then(() => { + expect(fired).to.eq(false) + }) + + cy.get(':text:first') + .invoke('val', 'bar') + .type('{selectAll}{leftarrow}{backspace}') + .then(() => { + expect(fired).to.eq(false) + }) + + cy.$$('textarea:first').on('input', () => { + fired = true + }) + + cy.get('textarea:first') + .invoke('val', 'bar') + .type('{selectAll}{rightarrow}{del}') + .then(() => { + expect(fired).to.eq(false) + }) + + cy.get('textarea:first') + .invoke('val', 'bar') + .type('{selectAll}{leftarrow}{backspace}') + .then(() => { + expect(fired).to.eq(false) + }) + + cy.$$('[contenteditable]:first').on('input', () => { + fired = true + }) + + cy.get('[contenteditable]:first') + .invoke('html', 'foobar') + .type('{movetoend}') + .then(($el) => { + expect(fired).to.eq(false) + }) + + cy.get('[contenteditable]:first') + .invoke('html', 'foobar') + .type('{selectAll}{leftarrow}{backspace}') + .then(() => { + expect(fired).to.eq(false) + }) + }) + }) + + describe('click events', () => { + it('passes timeout and interval down to click', (done) => { + const input = $('').attr('id', 'input-covered-in-span').prependTo(cy.$$('body')) + + $('span on input') + .css({ + position: 'absolute', + left: input.offset().left, + top: input.offset().top, + padding: 5, + display: 'inline-block', + backgroundColor: 'yellow', + }) + .prependTo(cy.$$('body')) + + cy.on('command:retry', (options) => { + expect(options.timeout).to.eq(1000) + expect(options.interval).to.eq(60) + + done() + }) + + cy.get('#input-covered-in-span').type('foobar', { timeout: 1000, interval: 60 }) + }) + + it('does not issue another click event between type/type', () => { + const clicked = cy.stub() + + cy.$$(':text:first').click(clicked) + + cy.get(':text:first').type('f').type('o').then(() => { + expect(clicked).to.be.calledOnce + }) + }) + + it('does not issue another click event if element is already in focus from click', () => { + const clicked = cy.stub() + + cy.$$(':text:first').click(clicked) + + cy.get(':text:first').click().type('o').then(() => { + expect(clicked).to.be.calledOnce + }) + }) + }) + + describe('change events', () => { + it('fires when enter is pressed and value has changed', () => { + const changed = cy.stub() + + cy.$$(':text:first').change(changed) + + cy.get(':text:first').invoke('val', 'foo').type('bar{enter}').then(() => { + expect(changed).to.be.calledOnce + }) + }) + + it('fires twice when enter is pressed and then again after losing focus', () => { + const changed = cy.stub() + + cy.$$(':text:first').change(changed) + + cy.get(':text:first').invoke('val', 'foo').type('bar{enter}baz').blur().then(() => { + expect(changed).to.be.calledTwice + }) + }) + + it('fires when element loses focus due to another action (click)', () => { + const changed = cy.stub() + + cy.$$(':text:first').change(changed) + + cy + .get(':text:first').type('foo').then(() => { + expect(changed).not.to.be.called + }) + .get('button:first').click().then(() => { + expect(changed).to.be.calledOnce + }) + }) + + it('fires when element loses focus due to another action (type)', () => { + const changed = cy.stub() + + cy.$$(':text:first').change(changed) + + cy + .get(':text:first').type('foo').then(() => { + expect(changed).not.to.be.called + }) + .get('textarea:first').type('bar').then(() => { + expect(changed).to.be.calledOnce + }) + }) + + it('fires when element is directly blurred', () => { + const changed = cy.stub() + + cy.$$(':text:first').change(changed) + + cy + .get(':text:first').type('foo').blur().then(() => { + expect(changed).to.be.calledOnce + }) + }) + + it('fires when element is tabbed away from')//, -> + // changed = 0 + + // cy.$$(":text:first").change -> + // changed += 1 + + // cy.get(":text:first").invoke("val", "foo").type("b{tab}").then -> + // expect(changed).to.eq 1 + + it('does not fire twice if element is already in focus between type/type', () => { + const changed = cy.stub() + + cy.$$(':text:first').change(changed) + + cy.get(':text:first').invoke('val', 'foo').type('f').type('o{enter}').then(() => { + expect(changed).to.be.calledOnce + }) + }) + + it('does not fire twice if element is already in focus between clear/type', () => { + const changed = cy.stub() + + cy.$$(':text:first').change(changed) + + cy.get(':text:first').invoke('val', 'foo').clear().type('o{enter}').then(() => { + expect(changed).to.be.calledOnce + }) + }) + + it('does not fire twice if element is already in focus between click/type', () => { + const changed = cy.stub() + + cy.$$(':text:first').change(changed) + + cy.get(':text:first').invoke('val', 'foo').click().type('o{enter}').then(() => { + expect(changed).to.be.calledOnce + }) + }) + + it('does not fire twice if element is already in focus between type/click', () => { + const changed = cy.stub() + + cy.$$(':text:first').change(changed) + + cy.get(':text:first').invoke('val', 'foo').type('d{enter}').click().then(() => { + expect(changed).to.be.calledOnce + }) + }) + + it('does not fire at all between clear/type/click', () => { + const changed = cy.stub() + + cy.$$(':text:first').change(changed) + + cy.get(':text:first').invoke('val', 'foo').clear().type('o').click().then(($el) => { + expect(changed).not.to.be.called + + return $el + }).blur() + .then(() => { + expect(changed).to.be.calledOnce + }) + }) + + it('does not fire if {enter} is preventedDefault', () => { + const changed = cy.stub() + + cy.$$(':text:first').keypress((e) => { + if (e.which === 13) { + e.preventDefault() + } + }) + + cy.$$(':text:first').change(changed) + + cy.get(':text:first').invoke('val', 'foo').type('b{enter}').then(() => { + expect(changed).not.to.be.called + }) + }) + + it('does not fire when enter is pressed and value hasnt changed', () => { + const changed = cy.stub() + + cy.$$(':text:first').change(changed) + + cy.get(':text:first').invoke('val', 'foo').type('b{backspace}{enter}').then(() => { + expect(changed).not.to.be.called + }) + }) + + it('does not fire at the end of the type', () => { + const changed = cy.stub() + + cy.$$(':text:first').change(changed) + + cy + .get(':text:first').type('foo').then(() => { + expect(changed).not.to.be.called + }) + }) + + it('does not fire change event if value hasnt actually changed', () => { + const changed = cy.stub() + + cy.$$(':text:first').change(changed) + + cy + .get(':text:first').invoke('val', 'foo').type('{backspace}{backspace}oo{enter}').blur().then(() => { + expect(changed).not.to.be.called + }) + }) + + it('does not fire if mousedown is preventedDefault which prevents element from losing focus', () => { + const changed = cy.stub() + + cy.$$(':text:first').change(changed) + + cy.$$('textarea:first').mousedown(() => { + return false + }) + + cy + .get(':text:first').invoke('val', 'foo').type('bar') + .get('textarea:first').click().then(() => { + expect(changed).not.to.be.called + }) + }) + + it('does not fire hitting {enter} inside of a textarea', () => { + const changed = cy.stub() + + cy.$$('textarea:first').change(changed) + + cy + .get('textarea:first').type('foo{enter}bar').then(() => { + expect(changed).not.to.be.called + }) + }) + + it('does not fire hitting {enter} inside of [contenteditable]', () => { + const changed = cy.stub() + + cy.$$('[contenteditable]:first').change(changed) + + cy + .get('[contenteditable]:first').type('foo{enter}bar').then(() => { + expect(changed).not.to.be.called + }) + }) + + // [contenteditable] does not fire ANY change events ever. + it('does not fire at ALL for [contenteditable]', () => { + const changed = cy.stub() + + cy.$$('[contenteditable]:first').change(changed) + + cy + .get('[contenteditable]:first').type('foo') + .get('button:first').click().then(() => { + expect(changed).not.to.be.called + }) + }) + + it('does not fire on .clear() without blur', () => { + const changed = cy.stub() + + cy.$$('input:first').change(changed) + + cy.get('input:first').invoke('val', 'foo') + .clear() + .then(($el) => { + expect(changed).not.to.be.called + + return $el + }).type('foo') + .blur() + .then(() => { + expect(changed).not.to.be.called + }) + }) + + it('fires change for single value change inputs', () => { + const changed = cy.stub() + + cy.$$('input[type="date"]:first').change(changed) + + cy.get('input[type="date"]:first') + .type('1959-09-13') + .blur() + .then(() => { + expect(changed).to.be.calledOnce + }) + }) + + it('does not fire change for non-change single value input', () => { + const changed = cy.stub() + + cy.$$('input[type="date"]:first').change(changed) + + cy.get('input[type="date"]:first') + .invoke('val', '1959-09-13') + .type('1959-09-13') + .blur() + .then(() => { + expect(changed).not.to.be.called + }) + }) + + it('does not fire change for type\'d change that restores value', () => { + const changed = cy.stub() + + cy.$$('input:first').change(changed) + + cy.get('input:first') + .invoke('val', 'foo') + .type('{backspace}o') + .invoke('val', 'bar') + .type('{backspace}r') + .blur() + .then(() => { + expect(changed).not.to.be.called + }) + }) + }) + + // https://github.com/cypress-io/cypress/issues/19541 + describe(`type('{enter}') and click event on button-like elements`, () => { + beforeEach(() => { + cy.visit('fixtures/click-event-by-type.html') + }) + + describe('triggers', () => { + const targets = [ + '#target-button-tag', + '#target-input-button', + '#target-input-image', + '#target-input-reset', + '#target-input-submit', + ] + + targets.forEach((target) => { + it(target, () => { + cy.get(target).focus().type('{enter}') + + cy.get('li').should('have.length', 4) + cy.get('li').eq(0).should('have.text', 'keydown') + cy.get('li').eq(1).should('have.text', 'keypress') + cy.get('li').eq(2).should('have.text', 'click') + cy.get('li').eq(3).should('have.text', 'keyup') + }) + }) + }) + + describe('does not trigger', () => { + const targets = [ + '#target-input-checkbox', + '#target-input-radio', + ] + + targets.forEach((target) => { + it(target, () => { + cy.get(target).focus().type('{enter}') + + cy.get('li').should('have.length', 3) + cy.get('li').eq(0).should('have.text', 'keydown') + cy.get('li').eq(1).should('have.text', 'keypress') + cy.get('li').eq(2).should('have.text', 'keyup') + }) + }) + }) + }) + + describe(`type(' ') fires click event on button-like elements`, () => { + beforeEach(() => { + cy.visit('fixtures/click-event-by-type.html') + }) + + const targets = [ + '#target-button-tag', + '#target-input-button', + '#target-input-image', + '#target-input-reset', + '#target-input-submit', + ] + + describe(`triggers with single space`, () => { + targets.forEach((target) => { + it(target, () => { + const events = [] + + $(target).on('keydown keypress keyup click', (evt) => { + events.push(evt.type) + }) + + cy.get(target).focus().type(' ').then(() => { + expect(events).to.deep.eq([ + 'keydown', + 'keypress', + 'keyup', + 'click', + ]) + }) + + cy.get('li').eq(0).should('have.text', 'keydown') + cy.get('li').eq(1).should('have.text', 'keypress') + cy.get('li').eq(2).should('have.text', 'keyup') + cy.get('li').eq(3).should('have.text', 'click') + }) + }) + }) + + describe(`does not trigger if keyup prevented`, () => { + targets.forEach((target) => { + it(`${target} does not fire click event`, () => { + const events = [] + + $(target) + .on('keydown keypress keyup click', (evt) => { + events.push(evt.type) + }) + .on('keyup', (evt) => { + evt.preventDefault() + }) + + cy.get(target).focus().type(' ').then(() => { + expect(events).to.deep.eq([ + 'keydown', + 'keypress', + 'keyup', + ]) + }) + + cy.get('li').should('have.length', 3) + cy.get('li').eq(0).should('have.text', 'keydown') + cy.get('li').eq(1).should('have.text', 'keypress') + cy.get('li').eq(2).should('have.text', 'keyup') + }) + }) + }) + + describe('triggers after other characters', () => { + targets.forEach((target) => { + it(target, () => { + const events = [] + + $(target).on('keydown keypress keyup click', (evt) => { + events.push(evt.type) + }) + + cy.get(target).focus().type('asd ').then(() => { + expect(events).to.deep.eq([ + 'keydown', + 'keypress', + 'keyup', + 'keydown', + 'keypress', + 'keyup', + 'keydown', + 'keypress', + 'keyup', + 'keydown', + 'keypress', + 'keyup', + 'click', + ]) + }) + + cy.get('li').eq(12).should('have.text', 'click') + }) + }) + }) + + describe('checkbox', () => { + it('checkbox is checked/unchecked', () => { + cy.get(`#target-input-checkbox`).focus().type(' ') + + cy.get('li').eq(0).should('have.text', 'keydown') + cy.get('li').eq(1).should('have.text', 'keypress') + cy.get('li').eq(2).should('have.text', 'keyup') + cy.get('li').eq(3).should('have.text', 'click') + + cy.get('#target-input-checkbox').should('be.checked') + + cy.get(`#target-input-checkbox`).type(' ') + + cy.get('li').eq(4).should('have.text', 'keydown') + cy.get('li').eq(5).should('have.text', 'keypress') + cy.get('li').eq(6).should('have.text', 'keyup') + cy.get('li').eq(7).should('have.text', 'click') + + cy.get('#target-input-checkbox').should('not.be.checked') + }) + }) + + describe('radio', () => { + it('radio fires click event when it is not checked', () => { + cy.get(`#target-input-radio`).focus().type(' ') + + cy.get('li').eq(0).should('have.text', 'keydown') + cy.get('li').eq(1).should('have.text', 'keypress') + cy.get('li').eq(2).should('have.text', 'keyup') + cy.get('li').eq(3).should('have.text', 'click') + + cy.get('#target-input-radio').should('be.checked') + }) + + it('radio does not fire click event when it is checked', () => { + // We're clicking here first to make the radio element checked. + cy.get(`#target-input-radio`).click().type(' ') + + // item 0 is click event. It's fired because we want to make sure our radio button is checked. + cy.get('li').eq(1).should('have.text', 'keydown') + cy.get('li').eq(2).should('have.text', 'keypress') + cy.get('li').eq(3).should('have.text', 'keyup') + + cy.get('#target-input-radio').should('be.checked') + }) + }) + }) +}) diff --git a/packages/driver/cypress/integration/commands/actions/type_spec.js b/packages/driver/cypress/integration/commands/actions/type_spec.js index 90e10798bb..20874e019a 100644 --- a/packages/driver/cypress/integration/commands/actions/type_spec.js +++ b/packages/driver/cypress/integration/commands/actions/type_spec.js @@ -557,199 +557,6 @@ describe('src/cy/commands/actions/type - #type', () => { }) }) - // https://github.com/cypress-io/cypress/issues/19541 - describe(`type('{enter}') and click event on button-like elements`, () => { - beforeEach(() => { - cy.visit('fixtures/click-event-by-type.html') - }) - - describe('triggers', () => { - const targets = [ - 'button-tag', - 'input-button', - 'input-image', - 'input-reset', - 'input-submit', - ] - - targets.forEach((targetId) => { - it(`${targetId}`, () => { - cy.get(`#target-${targetId}`).focus().type('{enter}') - - cy.get('li').eq(0).should('have.text', 'keydown') - cy.get('li').eq(1).should('have.text', 'keypress') - cy.get('li').eq(2).should('have.text', 'click') - cy.get('li').eq(3).should('have.text', 'keyup') - }) - }) - }) - - describe('does not trigger', () => { - const targets = [ - 'input-checkbox', - 'input-radio', - ] - - targets.forEach((targetId) => { - it(`${targetId}`, () => { - cy.get(`#target-${targetId}`).focus().type('{enter}') - - cy.get('li').eq(0).should('have.text', 'keydown') - cy.get('li').eq(1).should('have.text', 'keypress') - cy.get('li').eq(2).should('have.text', 'keyup') - }) - }) - }) - }) - - describe(`type(' ') fires click event on button-like elements`, () => { - beforeEach(() => { - cy.visit('fixtures/click-event-by-type.html') - }) - - const targets = [ - '#target-button-tag', - '#target-input-button', - '#target-input-image', - '#target-input-reset', - '#target-input-submit', - ] - - describe(`triggers with single space`, () => { - targets.forEach((target) => { - it(target, () => { - const events = [] - - $(target).on('keydown keypress keyup click', (evt) => { - events.push(evt.type) - }) - - cy.get(target).focus().type(' ').then(() => { - expect(events).to.deep.eq([ - 'keydown', - 'keypress', - 'keyup', - 'click', - ]) - }) - - cy.get('li').eq(0).should('have.text', 'keydown') - cy.get('li').eq(1).should('have.text', 'keypress') - cy.get('li').eq(2).should('have.text', 'keyup') - cy.get('li').eq(3).should('have.text', 'click') - }) - }) - }) - - describe(`does not trigger if keyup prevented`, () => { - targets.forEach((target) => { - it(`${target} does not fire click event`, () => { - const events = [] - - $(target) - .on('keydown keypress keyup click', (evt) => { - events.push(evt.type) - }) - .on('keyup', (evt) => { - evt.preventDefault() - }) - - cy.get(target).focus().type(' ').then(() => { - expect(events).to.deep.eq([ - 'keydown', - 'keypress', - 'keyup', - ]) - }) - - cy.get('li').should('have.length', 3) - cy.get('li').eq(0).should('have.text', 'keydown') - cy.get('li').eq(1).should('have.text', 'keypress') - cy.get('li').eq(2).should('have.text', 'keyup') - }) - }) - }) - - describe('triggers after other characters', () => { - targets.forEach((target) => { - it(target, () => { - const events = [] - - $(target).on('keydown keypress keyup click', (evt) => { - events.push(evt.type) - }) - - cy.get(target).focus().type('asd ').then(() => { - expect(events).to.deep.eq([ - 'keydown', - 'keypress', - 'keyup', - 'keydown', - 'keypress', - 'keyup', - 'keydown', - 'keypress', - 'keyup', - 'keydown', - 'keypress', - 'keyup', - 'click', - ]) - }) - - cy.get('li').eq(12).should('have.text', 'click') - }) - }) - }) - - describe('checkbox', () => { - it('checkbox is checked/unchecked', () => { - cy.get(`#target-input-checkbox`).focus().type(' ') - - cy.get('li').eq(0).should('have.text', 'keydown') - cy.get('li').eq(1).should('have.text', 'keypress') - cy.get('li').eq(2).should('have.text', 'keyup') - cy.get('li').eq(3).should('have.text', 'click') - - cy.get('#target-input-checkbox').should('be.checked') - - cy.get(`#target-input-checkbox`).type(' ') - - cy.get('li').eq(4).should('have.text', 'keydown') - cy.get('li').eq(5).should('have.text', 'keypress') - cy.get('li').eq(6).should('have.text', 'keyup') - cy.get('li').eq(7).should('have.text', 'click') - - cy.get('#target-input-checkbox').should('not.be.checked') - }) - }) - - describe('radio', () => { - it('radio fires click event when it is not checked', () => { - cy.get(`#target-input-radio`).focus().type(' ') - - cy.get('li').eq(0).should('have.text', 'keydown') - cy.get('li').eq(1).should('have.text', 'keypress') - cy.get('li').eq(2).should('have.text', 'keyup') - cy.get('li').eq(3).should('have.text', 'click') - - cy.get('#target-input-radio').should('be.checked') - }) - - it('radio does not fire click event when it is checked', () => { - // We're clicking here first to make the radio element checked. - cy.get(`#target-input-radio`).click().type(' ') - - // item 0 is click event. It's fired because we want to make sure our radio button is checked. - cy.get('li').eq(1).should('have.text', 'keydown') - cy.get('li').eq(2).should('have.text', 'keypress') - cy.get('li').eq(3).should('have.text', 'keyup') - - cy.get('#target-input-radio').should('be.checked') - }) - }) - }) - describe('tabindex', () => { beforeEach(function () { this.$div = cy.$$('#tabindex') @@ -979,243 +786,6 @@ describe('src/cy/commands/actions/type - #type', () => { }) }) - describe('events', () => { - it('receives keydown event', (done) => { - const $txt = cy.$$(':text:first') - - $txt.on('keydown', (e) => { - expect(_.toPlainObject(e.originalEvent)).to.include({ - altKey: false, - bubbles: true, - cancelable: true, - charCode: 0, // deprecated - ctrlKey: false, - detail: 0, - key: 'a', - // has code property https://github.com/cypress-io/cypress/issues/3722 - code: 'KeyA', - keyCode: 65, // deprecated but fired by chrome always uppercase in the ASCII table - location: 0, - metaKey: false, - repeat: false, - shiftKey: false, - type: 'keydown', - which: 65, // deprecated but fired by chrome - }) - - done() - }) - - cy.get(':text:first').type('a') - }) - - it('receives keypress event', (done) => { - const $txt = cy.$$(':text:first') - - $txt.on('keypress', (e) => { - expect(_.toPlainObject(e.originalEvent)).to.include({ - altKey: false, - bubbles: true, - cancelable: true, - charCode: 97, // deprecated - ctrlKey: false, - detail: 0, - key: 'a', - code: 'KeyA', - keyCode: 97, // deprecated - location: 0, - metaKey: false, - repeat: false, - shiftKey: false, - type: 'keypress', - which: 97, // deprecated - }) - - done() - }) - - cy.get(':text:first').type('a') - }) - - it('receives keyup event', (done) => { - const $txt = cy.$$(':text:first') - - $txt.on('keyup', (e) => { - expect(_.toPlainObject(e.originalEvent)).to.include({ - altKey: false, - bubbles: true, - cancelable: true, - charCode: 0, // deprecated - ctrlKey: false, - detail: 0, - key: 'a', - code: 'KeyA', - keyCode: 65, // deprecated but fired by chrome always uppercase in the ASCII table - location: 0, - metaKey: false, - repeat: false, - shiftKey: false, - type: 'keyup', - view: cy.state('window'), - which: 65, // deprecated but fired by chrome - }) - .not.have.property('inputType') - - done() - }) - - cy.get(':text:first').type('a') - }) - - it('receives textInput event', (done) => { - const $txt = cy.$$(':text:first') - - $txt[0].addEventListener('textInput', (e) => { - // FIXME: (firefox) firefox cannot access window objects else throw cross-origin error - expect(Object.prototype.toString.call(e.view)).eq('[object Window]') - e.view = null - expect(_.toPlainObject(e)).to.include({ - bubbles: true, - cancelable: true, - data: 'a', - detail: 0, - type: 'textInput', - // view: cy.state('window'), - which: 0, - }) - - done() - }) - - cy.get(':text:first').type('a') - }) - - it('receives input event', (done) => { - const $txt = cy.$$(':text:first') - - $txt.on('input', (e) => { - const obj = _.pick(e.originalEvent, 'bubbles', 'cancelable', 'type') - - expect(obj).to.deep.eq({ - bubbles: true, - cancelable: false, - type: 'input', - }) - - done() - }) - - cy.get(':text:first').type('a') - }) - - it('fires events in the correct order') - - it('fires events for each key stroke') - - it('does fire input event when value changes', () => { - const onInput = cy.stub() - - cy.$$(':text:first').on('input', onInput) - - cy.get(':text:first') - .invoke('val', 'bar') - .type('{selectAll}{rightarrow}{backspace}') - .then(() => { - expect(onInput).to.be.calledOnce - }) - .then(() => { - onInput.resetHistory() - }) - - cy.get(':text:first') - .invoke('val', 'bar') - .type('{selectAll}{leftarrow}{del}') - .then(() => { - expect(onInput).to.be.calledOnce - }) - .then(() => { - onInput.resetHistory() - }) - - cy.$$('[contenteditable]:first').on('input', onInput) - - cy.get('[contenteditable]:first') - .invoke('html', 'foobar') - .type('{selectAll}{rightarrow}{backspace}') - .then(() => { - expect(onInput).to.be.calledOnce - }) - .then(() => { - onInput.resetHistory() - }) - - cy.get('[contenteditable]:first') - .invoke('html', 'foobar') - .type('{selectAll}{leftarrow}{del}') - .then(() => { - expect(onInput).to.be.calledOnce - }) - }) - - it('does not fire input event when value does not change', () => { - let fired = false - - cy.$$(':text:first').on('input', () => { - fired = true - }) - - cy.get(':text:first') - .invoke('val', 'bar') - .type('{selectAll}{rightarrow}{del}') - .then(() => { - expect(fired).to.eq(false) - }) - - cy.get(':text:first') - .invoke('val', 'bar') - .type('{selectAll}{leftarrow}{backspace}') - .then(() => { - expect(fired).to.eq(false) - }) - - cy.$$('textarea:first').on('input', () => { - fired = true - }) - - cy.get('textarea:first') - .invoke('val', 'bar') - .type('{selectAll}{rightarrow}{del}') - .then(() => { - expect(fired).to.eq(false) - }) - - cy.get('textarea:first') - .invoke('val', 'bar') - .type('{selectAll}{leftarrow}{backspace}') - .then(() => { - expect(fired).to.eq(false) - }) - - cy.$$('[contenteditable]:first').on('input', () => { - fired = true - }) - - cy.get('[contenteditable]:first') - .invoke('html', 'foobar') - .type('{movetoend}') - .then(($el) => { - expect(fired).to.eq(false) - }) - - cy.get('[contenteditable]:first') - .invoke('html', 'foobar') - .type('{selectAll}{leftarrow}{backspace}') - .then(() => { - expect(fired).to.eq(false) - }) - }) - }) - describe('maxlength', () => { it('limits text entered to the maxlength attribute of a text input', () => { const $input = cy.$$(':text:first') @@ -2876,337 +2446,6 @@ describe('src/cy/commands/actions/type - #type', () => { }) }) - describe('click events', () => { - it('passes timeout and interval down to click', (done) => { - const input = $('').attr('id', 'input-covered-in-span').prependTo(cy.$$('body')) - - $('span on input') - .css({ - position: 'absolute', - left: input.offset().left, - top: input.offset().top, - padding: 5, - display: 'inline-block', - backgroundColor: 'yellow', - }) - .prependTo(cy.$$('body')) - - cy.on('command:retry', (options) => { - expect(options.timeout).to.eq(1000) - expect(options.interval).to.eq(60) - - done() - }) - - cy.get('#input-covered-in-span').type('foobar', { timeout: 1000, interval: 60 }) - }) - - it('does not issue another click event between type/type', () => { - const clicked = cy.stub() - - cy.$$(':text:first').click(clicked) - - cy.get(':text:first').type('f').type('o').then(() => { - expect(clicked).to.be.calledOnce - }) - }) - - it('does not issue another click event if element is already in focus from click', () => { - const clicked = cy.stub() - - cy.$$(':text:first').click(clicked) - - cy.get(':text:first').click().type('o').then(() => { - expect(clicked).to.be.calledOnce - }) - }) - }) - - describe('change events', () => { - it('fires when enter is pressed and value has changed', () => { - const changed = cy.stub() - - cy.$$(':text:first').change(changed) - - cy.get(':text:first').invoke('val', 'foo').type('bar{enter}').then(() => { - expect(changed).to.be.calledOnce - }) - }) - - it('fires twice when enter is pressed and then again after losing focus', () => { - const changed = cy.stub() - - cy.$$(':text:first').change(changed) - - cy.get(':text:first').invoke('val', 'foo').type('bar{enter}baz').blur().then(() => { - expect(changed).to.be.calledTwice - }) - }) - - it('fires when element loses focus due to another action (click)', () => { - const changed = cy.stub() - - cy.$$(':text:first').change(changed) - - cy - .get(':text:first').type('foo').then(() => { - expect(changed).not.to.be.called - }) - .get('button:first').click().then(() => { - expect(changed).to.be.calledOnce - }) - }) - - it('fires when element loses focus due to another action (type)', () => { - const changed = cy.stub() - - cy.$$(':text:first').change(changed) - - cy - .get(':text:first').type('foo').then(() => { - expect(changed).not.to.be.called - }) - .get('textarea:first').type('bar').then(() => { - expect(changed).to.be.calledOnce - }) - }) - - it('fires when element is directly blurred', () => { - const changed = cy.stub() - - cy.$$(':text:first').change(changed) - - cy - .get(':text:first').type('foo').blur().then(() => { - expect(changed).to.be.calledOnce - }) - }) - - it('fires when element is tabbed away from')//, -> - // changed = 0 - - // cy.$$(":text:first").change -> - // changed += 1 - - // cy.get(":text:first").invoke("val", "foo").type("b{tab}").then -> - // expect(changed).to.eq 1 - - it('does not fire twice if element is already in focus between type/type', () => { - const changed = cy.stub() - - cy.$$(':text:first').change(changed) - - cy.get(':text:first').invoke('val', 'foo').type('f').type('o{enter}').then(() => { - expect(changed).to.be.calledOnce - }) - }) - - it('does not fire twice if element is already in focus between clear/type', () => { - const changed = cy.stub() - - cy.$$(':text:first').change(changed) - - cy.get(':text:first').invoke('val', 'foo').clear().type('o{enter}').then(() => { - expect(changed).to.be.calledOnce - }) - }) - - it('does not fire twice if element is already in focus between click/type', () => { - const changed = cy.stub() - - cy.$$(':text:first').change(changed) - - cy.get(':text:first').invoke('val', 'foo').click().type('o{enter}').then(() => { - expect(changed).to.be.calledOnce - }) - }) - - it('does not fire twice if element is already in focus between type/click', () => { - const changed = cy.stub() - - cy.$$(':text:first').change(changed) - - cy.get(':text:first').invoke('val', 'foo').type('d{enter}').click().then(() => { - expect(changed).to.be.calledOnce - }) - }) - - it('does not fire at all between clear/type/click', () => { - const changed = cy.stub() - - cy.$$(':text:first').change(changed) - - cy.get(':text:first').invoke('val', 'foo').clear().type('o').click().then(($el) => { - expect(changed).not.to.be.called - - return $el - }).blur() - .then(() => { - expect(changed).to.be.calledOnce - }) - }) - - it('does not fire if {enter} is preventedDefault', () => { - const changed = cy.stub() - - cy.$$(':text:first').keypress((e) => { - if (e.which === 13) { - e.preventDefault() - } - }) - - cy.$$(':text:first').change(changed) - - cy.get(':text:first').invoke('val', 'foo').type('b{enter}').then(() => { - expect(changed).not.to.be.called - }) - }) - - it('does not fire when enter is pressed and value hasnt changed', () => { - const changed = cy.stub() - - cy.$$(':text:first').change(changed) - - cy.get(':text:first').invoke('val', 'foo').type('b{backspace}{enter}').then(() => { - expect(changed).not.to.be.called - }) - }) - - it('does not fire at the end of the type', () => { - const changed = cy.stub() - - cy.$$(':text:first').change(changed) - - cy - .get(':text:first').type('foo').then(() => { - expect(changed).not.to.be.called - }) - }) - - it('does not fire change event if value hasnt actually changed', () => { - const changed = cy.stub() - - cy.$$(':text:first').change(changed) - - cy - .get(':text:first').invoke('val', 'foo').type('{backspace}{backspace}oo{enter}').blur().then(() => { - expect(changed).not.to.be.called - }) - }) - - it('does not fire if mousedown is preventedDefault which prevents element from losing focus', () => { - const changed = cy.stub() - - cy.$$(':text:first').change(changed) - - cy.$$('textarea:first').mousedown(() => { - return false - }) - - cy - .get(':text:first').invoke('val', 'foo').type('bar') - .get('textarea:first').click().then(() => { - expect(changed).not.to.be.called - }) - }) - - it('does not fire hitting {enter} inside of a textarea', () => { - const changed = cy.stub() - - cy.$$('textarea:first').change(changed) - - cy - .get('textarea:first').type('foo{enter}bar').then(() => { - expect(changed).not.to.be.called - }) - }) - - it('does not fire hitting {enter} inside of [contenteditable]', () => { - const changed = cy.stub() - - cy.$$('[contenteditable]:first').change(changed) - - cy - .get('[contenteditable]:first').type('foo{enter}bar').then(() => { - expect(changed).not.to.be.called - }) - }) - - // [contenteditable] does not fire ANY change events ever. - it('does not fire at ALL for [contenteditable]', () => { - const changed = cy.stub() - - cy.$$('[contenteditable]:first').change(changed) - - cy - .get('[contenteditable]:first').type('foo') - .get('button:first').click().then(() => { - expect(changed).not.to.be.called - }) - }) - - it('does not fire on .clear() without blur', () => { - const changed = cy.stub() - - cy.$$('input:first').change(changed) - - cy.get('input:first').invoke('val', 'foo') - .clear() - .then(($el) => { - expect(changed).not.to.be.called - - return $el - }).type('foo') - .blur() - .then(() => { - expect(changed).not.to.be.called - }) - }) - - it('fires change for single value change inputs', () => { - const changed = cy.stub() - - cy.$$('input[type="date"]:first').change(changed) - - cy.get('input[type="date"]:first') - .type('1959-09-13') - .blur() - .then(() => { - expect(changed).to.be.calledOnce - }) - }) - - it('does not fire change for non-change single value input', () => { - const changed = cy.stub() - - cy.$$('input[type="date"]:first').change(changed) - - cy.get('input[type="date"]:first') - .invoke('val', '1959-09-13') - .type('1959-09-13') - .blur() - .then(() => { - expect(changed).not.to.be.called - }) - }) - - it('does not fire change for type\'d change that restores value', () => { - const changed = cy.stub() - - cy.$$('input:first').change(changed) - - cy.get('input:first') - .invoke('val', 'foo') - .type('{backspace}o') - .invoke('val', 'bar') - .type('{backspace}r') - .blur() - .then(() => { - expect(changed).not.to.be.called - }) - }) - }) - describe('single value change inputs', () => { // https://github.com/cypress-io/cypress/issues/5476 it('fires all keyboard events', () => { From 66229039b87dd96fa8f3150904154f78ec5d8da3 Mon Sep 17 00:00:00 2001 From: KHeo Date: Wed, 16 Feb 2022 11:00:07 +0900 Subject: [PATCH 2/8] feedback --- .../cypress/integration/commands/actions/type_events_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/driver/cypress/integration/commands/actions/type_events_spec.js b/packages/driver/cypress/integration/commands/actions/type_events_spec.js index 697c1dd670..58e281b92b 100644 --- a/packages/driver/cypress/integration/commands/actions/type_events_spec.js +++ b/packages/driver/cypress/integration/commands/actions/type_events_spec.js @@ -5,7 +5,7 @@ describe('src/cy/commands/actions/type - #type events', () => { cy.visit('fixtures/dom.html') }) - describe('events', () => { + describe('keyboard events', () => { it('receives keydown event', (done) => { const $txt = cy.$$(':text:first') From c944ac6e72b874a8a0d63c475236acd545831451 Mon Sep 17 00:00:00 2001 From: KHeo Date: Mon, 21 Feb 2022 09:34:32 +0900 Subject: [PATCH 3/8] Add TODO comments --- .../cypress/integration/commands/actions/type_events_spec.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/driver/cypress/integration/commands/actions/type_events_spec.js b/packages/driver/cypress/integration/commands/actions/type_events_spec.js index 58e281b92b..240c687f7c 100644 --- a/packages/driver/cypress/integration/commands/actions/type_events_spec.js +++ b/packages/driver/cypress/integration/commands/actions/type_events_spec.js @@ -134,6 +134,8 @@ describe('src/cy/commands/actions/type - #type events', () => { cy.get(':text:first').type('a') }) + // https://github.com/cypress-io/cypress/issues/20283 + // TODO: Implement tests below. it('fires events in the correct order') it('fires events for each key stroke') @@ -348,6 +350,8 @@ describe('src/cy/commands/actions/type - #type events', () => { }) }) + // https://github.com/cypress-io/cypress/issues/20283 + // TODO: implement this test it('fires when element is tabbed away from')//, -> // changed = 0 From 27e3dc3dc55146dbfc18602988563421ed257d31 Mon Sep 17 00:00:00 2001 From: Zach Bloomquist Date: Thu, 24 Feb 2022 09:07:44 -0500 Subject: [PATCH 4/8] chore: fix CI cache state for darwin (#20339) --- circle.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/circle.yml b/circle.yml index 2edd9135a6..881c37f7ee 100644 --- a/circle.yml +++ b/circle.yml @@ -29,7 +29,7 @@ mainBuildFilters: &mainBuildFilters only: - develop - 10.0-release - - fix-darwin-win32-node-modules-install + - fix-cache-state # usually we don't build Mac app - it takes a long time # but sometimes we want to really confirm we are doing the right thing @@ -38,7 +38,7 @@ macWorkflowFilters: &mac-workflow-filters when: or: - equal: [ develop, << pipeline.git.branch >> ] - - equal: [ fix-darwin-win32-node-modules-install, << pipeline.git.branch >> ] + - equal: [ fix-cache-state, << pipeline.git.branch >> ] - matches: pattern: "-release$" value: << pipeline.git.branch >> @@ -48,7 +48,7 @@ windowsWorkflowFilters: &windows-workflow-filters or: - equal: [ master, << pipeline.git.branch >> ] - equal: [ develop, << pipeline.git.branch >> ] - - equal: [ fix-darwin-win32-node-modules-install, << pipeline.git.branch >> ] + - equal: [ fix-cache-state, << pipeline.git.branch >> ] - matches: pattern: "-release$" value: << pipeline.git.branch >> @@ -265,7 +265,7 @@ commands: - run: name: Bail if cache exists command: | - if [[ -f "/tmp/node_modules_installed" ]]; then + if [[ -f "node_modules_installed" ]]; then echo "Node modules already cached for dependencies, exiting" circleci-agent step halt fi @@ -300,12 +300,12 @@ commands: key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-node-modules-cache-{{ checksum "circle_cache_key" }} paths: - /tmp/node_modules_cache - - run: touch /tmp/node_modules_installed + - run: touch node_modules_installed - save_cache: name: Saving node-modules cache state key key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-node-modules-cache-state-{{ checksum "circle_cache_key" }} paths: - - /tmp/node_modules_installed + - node_modules_installed - save_cache: name: Save weekly yarn cache key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-deps-root-weekly-{{ checksum "cache_date" }} @@ -1626,7 +1626,7 @@ jobs: - run: name: Check current branch to persist artifacts command: | - if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "fix-darwin-win32-node-modules-install" ]]; then + if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "fix-cache-state" ]]; then echo "Not uploading artifacts or posting install comment for this branch." circleci-agent step halt fi From 8e420b618cd46b6d11ff0ef0b7785185ed50dbd1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 24 Feb 2022 10:36:49 -0600 Subject: [PATCH 5/8] chore: Update Chrome (beta) to 99.0.4844.45 (#20234) Co-authored-by: cypress-bot[bot] <2f0651858c6e38e0+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Emily Rohrbough --- browser-versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser-versions.json b/browser-versions.json index b2731c658f..3af828b9c8 100644 --- a/browser-versions.json +++ b/browser-versions.json @@ -1,4 +1,4 @@ { - "chrome:beta": "99.0.4844.27", + "chrome:beta": "99.0.4844.45", "chrome:stable": "98.0.4758.102" } From 95700aed467b9336e768a757f8a9937a7c43c2c2 Mon Sep 17 00:00:00 2001 From: Zach Bloomquist Date: Fri, 25 Feb 2022 10:58:49 -0500 Subject: [PATCH 6/8] chore: fix "cannot find module" in clone-repo-and-checkout-release-branch (#20293) --- circle.yml | 18 ++++++++++++------ scripts/get-next-version.js | 22 +++++++++++++--------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/circle.yml b/circle.yml index 881c37f7ee..eb597e8a69 100644 --- a/circle.yml +++ b/circle.yml @@ -29,7 +29,7 @@ mainBuildFilters: &mainBuildFilters only: - develop - 10.0-release - - fix-cache-state + - fix-next-version-in-test-repos # usually we don't build Mac app - it takes a long time # but sometimes we want to really confirm we are doing the right thing @@ -38,7 +38,7 @@ macWorkflowFilters: &mac-workflow-filters when: or: - equal: [ develop, << pipeline.git.branch >> ] - - equal: [ fix-cache-state, << pipeline.git.branch >> ] + - equal: [ fix-next-version-in-test-repos, << pipeline.git.branch >> ] - matches: pattern: "-release$" value: << pipeline.git.branch >> @@ -48,7 +48,7 @@ windowsWorkflowFilters: &windows-workflow-filters or: - equal: [ master, << pipeline.git.branch >> ] - equal: [ develop, << pipeline.git.branch >> ] - - equal: [ fix-cache-state, << pipeline.git.branch >> ] + - equal: [ fix-next-version-in-test-repos, << pipeline.git.branch >> ] - matches: pattern: "-release$" value: << pipeline.git.branch >> @@ -586,10 +586,16 @@ commands: steps: - restore_cached_binary - run: - name: "Cloning test project: <>" + name: "Cloning test project and checking out release branch: <>" + working_directory: ~/ command: | git clone --depth 1 --no-single-branch https://github.com/cypress-io/<>.git /tmp/<> - cd /tmp/<> && (git checkout $(node ./scripts/get-next-version.js) || true) + + # install some deps for get-next-version + npm i semver@7.3.2 conventional-recommended-bump@6.1.0 conventional-changelog-angular@5.0.12 + NEXT_VERSION=$(node ./cypress/scripts/get-next-version.js) + + cd /tmp/<> && (git checkout $NEXT_VERSION || true) test-binary-against-rwa: description: | @@ -1626,7 +1632,7 @@ jobs: - run: name: Check current branch to persist artifacts command: | - if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "fix-cache-state" ]]; then + if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "fix-next-version-in-test-repos" ]]; then echo "Not uploading artifacts or posting install comment for this branch." circleci-agent step halt fi diff --git a/scripts/get-next-version.js b/scripts/get-next-version.js index 949e9ba7fc..bb8810b9c0 100644 --- a/scripts/get-next-version.js +++ b/scripts/get-next-version.js @@ -2,13 +2,14 @@ // See ../guides/next-version.md for documentation. +const path = require('path') const semver = require('semver') -const Bluebird = require('bluebird') const bumpCb = require('conventional-recommended-bump') +const { promisify } = require('util') const currentVersion = require('../package.json').version -const bump = Bluebird.promisify(bumpCb) +const bump = promisify(bumpCb) const paths = ['packages', 'cli'] let nextVersion @@ -30,14 +31,17 @@ if (require.main !== module) { return } -Bluebird.mapSeries(paths, async (path) => { - const pathNextVersion = await getNextVersionForPath(path) +(async () => { + process.chdir(path.join(__dirname, '..')) - if (!nextVersion || semver.gt(pathNextVersion, nextVersion)) { - nextVersion = pathNextVersion + for (const path of paths) { + const pathNextVersion = await getNextVersionForPath(path) + + if (!nextVersion || semver.gt(pathNextVersion, nextVersion)) { + nextVersion = pathNextVersion + } } -}) -.then(() => { + if (!nextVersion) { throw new Error('Unable to determine next version.') } @@ -51,4 +55,4 @@ Bluebird.mapSeries(paths, async (path) => { } console.log(nextVersion) -}) +})() From 995798c3f93f5c4efb251fdde8c077ba39800ca0 Mon Sep 17 00:00:00 2001 From: Kukhyeon Heo Date: Sat, 26 Feb 2022 07:42:31 +0900 Subject: [PATCH 7/8] chore: remove pkg/driver //@ts-nocheck final (#20169) --- .../integration/cypress/error_utils_spec.ts | 26 +- packages/driver/src/cypress.ts | 146 ++++++---- packages/driver/src/cypress/error_utils.ts | 6 +- packages/driver/src/cypress/log.ts | 258 +++++++++--------- packages/driver/src/cypress/mocha.ts | 20 +- packages/driver/src/cypress/runner.ts | 96 ++++--- packages/driver/src/cypress/screenshot.ts | 10 +- packages/driver/src/cypress/script_utils.ts | 10 +- .../driver/src/cypress/source_map_utils.ts | 4 +- packages/driver/src/cypress/stack_utils.ts | 35 ++- packages/driver/src/cypress/utils.ts | 11 +- .../driver/src/cypress/xml_http_request.ts | 12 +- packages/driver/src/dom/jquery.ts | 2 +- packages/driver/src/dom/window.ts | 2 - packages/driver/src/util/limited_map.ts | 6 +- packages/driver/types/internal-types.d.ts | 2 + packages/errors/src/stackUtils.ts | 2 +- 17 files changed, 375 insertions(+), 273 deletions(-) diff --git a/packages/driver/cypress/integration/cypress/error_utils_spec.ts b/packages/driver/cypress/integration/cypress/error_utils_spec.ts index fcd5a0b81d..55464da9bc 100644 --- a/packages/driver/cypress/integration/cypress/error_utils_spec.ts +++ b/packages/driver/cypress/integration/cypress/error_utils_spec.ts @@ -1,11 +1,9 @@ -// @ts-nocheck - import { allowTsModuleStubbing } from '../../support/helpers' allowTsModuleStubbing() import $stackUtils from '@packages/driver/src/cypress/stack_utils' -import $errUtils from '@packages/driver/src/cypress/error_utils' +import $errUtils, { CypressError } from '@packages/driver/src/cypress/error_utils' import $errorMessages from '@packages/driver/src/cypress/error_messages' describe('driver/src/cypress/error_utils', () => { @@ -76,6 +74,7 @@ describe('driver/src/cypress/error_utils', () => { it('throws error when it is an error', () => { const err = new Error('Something unexpected') + // @ts-ignore err.extraProp = 'extra prop' const fn = () => { $errUtils.throwErr(err) @@ -117,6 +116,7 @@ describe('driver/src/cypress/error_utils', () => { context('.errByPath', () => { beforeEach(() => { + // @ts-ignore $errorMessages.__test_errors = { obj: { message: 'This is a simple error message', @@ -186,7 +186,7 @@ describe('driver/src/cypress/error_utils', () => { describe('when message value is an object', () => { it('has correct name, message, and docs url when path exists', () => { - const err = $errUtils.errByPath('__test_errors.obj') + const err = $errUtils.errByPath('__test_errors.obj') as CypressError expect(err.name).to.eq('CypressError') expect(err.message).to.include('This is a simple error message') @@ -196,7 +196,7 @@ describe('driver/src/cypress/error_utils', () => { it('uses args provided for the error', () => { const err = $errUtils.errByPath('__test_errors.obj_with_args', { foo: 'foo', bar: ['bar', 'qux'], - }) + }) as CypressError expect(err.message).to.include('This has args like \'foo\' and bar,qux') expect(err.docsUrl).to.include('https://on.link.io') @@ -205,7 +205,7 @@ describe('driver/src/cypress/error_utils', () => { it('handles args being used multiple times in message', () => { const err = $errUtils.errByPath('__test_errors.obj_with_multi_args', { foo: 'foo', bar: ['bar', 'qux'], - }) + }) as CypressError expect(err.message).to.include('This has args like \'foo\' and bar,qux, and \'foo\' is used twice') expect(err.docsUrl).to.include('https://on.link.io') @@ -214,7 +214,7 @@ describe('driver/src/cypress/error_utils', () => { it('formats markdown in the error message', () => { const err = $errUtils.errByPath('__test_errors.obj_with_markdown', { foo: 'foo', bar: ['bar', 'qux'], - }) + }) as CypressError expect(err.message).to.include('This has markdown like `foo`, *bar,qux*, **foo**, and _bar,qux_') expect(err.docsUrl).to.include('https://on.link.io') @@ -223,7 +223,7 @@ describe('driver/src/cypress/error_utils', () => { describe('when message value is a string', () => { it('has correct name, message, and docs url', () => { - const err = $errUtils.errByPath('__test_errors.str') + const err = $errUtils.errByPath('__test_errors.str') as CypressError expect(err.name).to.eq('CypressError') expect(err.message).to.include('This is a simple error message') @@ -299,7 +299,7 @@ describe('driver/src/cypress/error_utils', () => { }) it('has the right message and docs url', () => { - const err = $errUtils.errByPath('__test_errors.fn_returns_obj') + const err = $errUtils.errByPath('__test_errors.fn_returns_obj') as CypressError expect(err.message).to.include('This is a simple error message') expect(err.docsUrl).to.include('https://on.link.io') @@ -310,7 +310,7 @@ describe('driver/src/cypress/error_utils', () => { it('uses them in the error message', () => { const err = $errUtils.errByPath('__test_errors.fn_returns_obj_with_args', { foo: 'foo', bar: ['bar', 'qux'], - }) + }) as CypressError expect(err.message).to.include('This has args like \'foo\' and bar,qux') expect(err.docsUrl).to.include('https://on.link.io') @@ -321,7 +321,7 @@ describe('driver/src/cypress/error_utils', () => { it('uses them in the error message', () => { const err = $errUtils.errByPath('__test_errors.fn_returns_obj_with_multi_args', { foo: 'foo', bar: ['bar', 'qux'], - }) + }) as CypressError expect(err.message).to.include('This has args like \'foo\' and bar,qux, and \'foo\' is used twice') expect(err.docsUrl).to.include('https://on.link.io') @@ -334,6 +334,7 @@ describe('driver/src/cypress/error_utils', () => { let fn beforeEach(() => { + // @ts-ignore $errorMessages.__test_errors = { test: 'Simple error {{message}}', } @@ -370,6 +371,7 @@ describe('driver/src/cypress/error_utils', () => { context('.throwErrByPath', () => { it('looks up error and throws it', () => { + // @ts-ignore $errorMessages.__test_error = 'simple error message' const fn = () => $errUtils.throwErrByPath('__test_error') @@ -598,7 +600,7 @@ describe('driver/src/cypress/error_utils', () => { context('Error.captureStackTrace', () => { it('works - even where not natively support', () => { function removeMe2 () { - const err = {} + const err: Record = {} Error.captureStackTrace(err, removeMeAndAbove) diff --git a/packages/driver/src/cypress.ts b/packages/driver/src/cypress.ts index 03a5eb4329..0e0b3ece78 100644 --- a/packages/driver/src/cypress.ts +++ b/packages/driver/src/cypress.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import { validate, validateNoReadOnlyConfig } from '@packages/config' import _ from 'lodash' import $ from 'jquery' @@ -42,6 +40,15 @@ import * as resolvers from './cypress/resolvers' const debug = debugFn('cypress:driver:cypress') +declare global { + interface Window { + __cySkipValidateConfig: boolean + Cypress: Cypress.Cypress + Runner: any + cy: Cypress.cy + } +} + const jqueryProxyFn = function (...args) { if (!this.cy) { $errUtils.throwErrByPath('miscellaneous.no_cy') @@ -56,7 +63,83 @@ const throwPrivateCommandInterface = (method) => { }) } +interface BackendError extends Error { + __stackCleaned__: boolean + backend: boolean +} + +interface AutomationError extends Error { + automation: boolean +} + class $Cypress { + cy: any + chai: any + mocha: any + runner: any + downloads: any + Commands: any + $autIframe: any + onSpecReady: any + events: any + $: any + arch: any + spec: any + version: any + browser: any + platform: any + testingType: any + state: any + originalConfig: any + config: any + env: any + getTestRetries: any + Cookies: any + ProxyLogging: any + _onInitialize: any + isCy: any + log: any + isBrowser: any + emit: any + emitThen: any + emitMap: any + + // attach to $Cypress to access + // all of the constructors + // to enable users to monkeypatch + $Cypress = $Cypress + Cy = $Cy + Chainer = $Chainer + Command = $Command + dom = $dom + errorMessages = $errorMessages + Keyboard = $Keyboard + Location = $Location + Log = $Log + LocalStorage = $LocalStorage + Mocha = $Mocha + resolveWindowReference = resolvers.resolveWindowReference + resolveLocationReference = resolvers.resolveLocationReference + Mouse = { + create: createMouse, + } + + Runner = $Runner + Server = $Server + Screenshot = $Screenshot + SelectorPlayground = $SelectorPlayground + utils = $utils + _ = _ + Blob = blobUtil + Buffer = Buffer + Promise = Promise + minimatch = minimatch + sinon = sinon + lolex = fakeTimers + + static $: any + static utils: any + constructor (config = {}) { this.cy = null this.chai = null @@ -75,7 +158,7 @@ class $Cypress { this.setConfig(config) } - setConfig (config = {}) { + setConfig (config: Record = {}) { // config.remote // { // origin: "http://localhost:2020" @@ -144,7 +227,7 @@ class $Cypress { this.state = $SetterGetter.create({}) this.originalConfig = _.cloneDeep(config) this.config = $SetterGetter.create(config, (config) => { - if (!window.top.__cySkipValidateConfig) { + if (!window.top!.__cySkipValidateConfig) { validateNoReadOnlyConfig(config, (errProperty) => { const errPath = this.state('runnable') ? 'config.invalid_cypress_config_override' @@ -191,6 +274,8 @@ class $Cypress { this.Cookies = $Cookies.create(config.namespace, d) + // TODO: Remove this after $Events functions are added to $Cypress. + // @ts-ignore this.ProxyLogging = new ProxyLogging(this) return this.action('cypress:config', config) @@ -216,15 +301,15 @@ class $Cypress { // Method to manually re-execute Runner (usually within $autIframe) // used mainly by Component Testing restartRunner () { - if (!window.top.Cypress) { + if (!window.top!.Cypress) { throw Error('Cannot re-run spec without Cypress') } // MobX state is only available on the Runner instance // which is attached to the top level `window` // We avoid infinite restart loop by checking if not in a loading state. - if (!window.top.Runner.state.isLoading) { - window.top.Runner.emit('restart') + if (!window.top!.Runner.state.isLoading) { + window.top!.Runner.emit('restart') } } @@ -249,6 +334,8 @@ class $Cypress { this.events.proxyTo(this.cy) $scriptUtils.runScripts(specWindow, scripts) + // TODO: remove this after making the type of `runScripts` more specific. + // @ts-ignore .catch((error) => { this.runner.onSpecError('error')({ error }) }) @@ -275,6 +362,8 @@ class $Cypress { return this.backend('firefox:window:focus') } } + + return }) .then(() => { this.cy.initialize(this.$autIframe) @@ -589,7 +678,7 @@ class $Cypress { // attaching long stace traces // which otherwise make this err // unusably long - const err = $errUtils.makeErrFromObj(e) + const err = $errUtils.makeErrFromObj(e) as BackendError err.__stackCleaned__ = true err.backend = true @@ -611,7 +700,7 @@ class $Cypress { const e = reply.error if (e) { - const err = $errUtils.makeErrFromObj(e) + const err = $errUtils.makeErrFromObj(e) as AutomationError err.automation = true @@ -670,43 +759,8 @@ class $Cypress { } } -// // attach to $Cypress to access -// // all of the constructors -// // to enable users to monkeypatch -$Cypress.prototype.$Cypress = $Cypress -$Cypress.prototype.Cy = $Cy -$Cypress.prototype.Chainer = $Chainer -$Cypress.prototype.Cookies = $Cookies -$Cypress.prototype.Command = $Command -$Cypress.prototype.Commands = $Commands -$Cypress.prototype.dom = $dom -$Cypress.prototype.errorMessages = $errorMessages -$Cypress.prototype.Keyboard = $Keyboard -$Cypress.prototype.Location = $Location -$Cypress.prototype.Log = $Log -$Cypress.prototype.LocalStorage = $LocalStorage -$Cypress.prototype.Mocha = $Mocha -$Cypress.prototype.resolveWindowReference = resolvers.resolveWindowReference -$Cypress.prototype.resolveLocationReference = resolvers.resolveLocationReference -$Cypress.prototype.Mouse = { - create: createMouse, -} - -$Cypress.prototype.Runner = $Runner -$Cypress.prototype.Server = $Server -$Cypress.prototype.Screenshot = $Screenshot -$Cypress.prototype.SelectorPlayground = $SelectorPlayground -$Cypress.prototype.utils = $utils -$Cypress.prototype._ = _ -$Cypress.prototype.Blob = blobUtil -$Cypress.prototype.Buffer = Buffer -$Cypress.prototype.Promise = Promise -$Cypress.prototype.minimatch = minimatch -$Cypress.prototype.sinon = sinon -$Cypress.prototype.lolex = fakeTimers - -// // attaching these so they are accessible -// // via the runner + integration spec helper +// attaching these so they are accessible +// via the runner + integration spec helper $Cypress.$ = $ $Cypress.utils = $utils export default $Cypress diff --git a/packages/driver/src/cypress/error_utils.ts b/packages/driver/src/cypress/error_utils.ts index 3d001726ec..11e79a7697 100644 --- a/packages/driver/src/cypress/error_utils.ts +++ b/packages/driver/src/cypress/error_utils.ts @@ -422,7 +422,11 @@ const preferredStackAndCodeFrameIndex = (err, userInvocationStack) => { return { stack, index } } -const enhanceStack = ({ err, userInvocationStack, projectRoot }) => { +const enhanceStack = ({ err, userInvocationStack, projectRoot }: { + err: any + userInvocationStack?: any + projectRoot?: any +}) => { const { stack, index } = preferredStackAndCodeFrameIndex(err, userInvocationStack) const { sourceMapped, parsed } = $stackUtils.getSourceStack(stack, projectRoot) diff --git a/packages/driver/src/cypress/log.ts b/packages/driver/src/cypress/log.ts index c5221f42f8..505926172e 100644 --- a/packages/driver/src/cypress/log.ts +++ b/packages/driver/src/cypress/log.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import _ from 'lodash' import $ from 'jquery' import clone from 'clone' @@ -86,7 +84,7 @@ const getSnapshotProps = (attrs) => { return _.pick(attrs, SNAPSHOT_PROPS) } -const countLogsByTests = function (tests = {}) { +const countLogsByTests = function (tests: Record = {}) { if (_.isEmpty(tests)) { return 0 } @@ -96,7 +94,7 @@ const countLogsByTests = function (tests = {}) { .flatMap((test) => { return [test, test.prevAttempts] }) - .flatMap((tests) => { + .flatMap<{id: number}>((tests) => { return [].concat(tests.agents, tests.routes, tests.commands) }).compact() .union([{ id: 0 }]) @@ -219,11 +217,12 @@ const defaults = function (state, config, obj) { return obj } +// TODO: Change `Log` function to class. const Log = function (cy, state, config, obj) { obj = defaults(state, config, obj) // private attributes of each log - const attributes = {} + const attributes: Record = {} return { get (attr) { @@ -267,7 +266,7 @@ const Log = function (cy, state, config, obj) { .value() }, - set (key, val) { + set (key, val?) { if (_.isString(key)) { obj = {} obj[key] = val @@ -318,7 +317,7 @@ const Log = function (cy, state, config, obj) { return _.pick(attributes, args) }, - snapshot (name, options = {}) { + snapshot (name?, options: any = {}) { // bail early and don't snapshot if we're in headless mode // or we're not storing tests if (!config('isInteractive') || (config('numTestsKeptInMemory') === 0)) { @@ -467,7 +466,7 @@ const Log = function (cy, state, config, obj) { attributes.consoleProps = function (...args) { const key = _this.get('event') ? 'Event' : 'Command' - const consoleObj = {} + const consoleObj: Record = {} consoleObj[key] = _this.get('name') @@ -497,6 +496,128 @@ const Log = function (cy, state, config, obj) { } } +function create (Cypress, cy, state, config) { + counter = 0 + const logs = {} + + const trigger = function (log, event) { + // bail if we never fired our initial log event + if (!log._hasInitiallyLogged) { + return + } + + // bail if we've reset the logs due to a Cypress.abort + if (!logs[log.get('id')]) { + return + } + + const attrs = log.toJSON() + + // only trigger this event if our last stored + // emitted attrs do not match the current toJSON + if (!_.isEqual(log._emittedAttrs, attrs)) { + log._emittedAttrs = attrs + + log.emit(event, attrs) + + return Cypress.action(event, attrs, log) + } + } + + const triggerLog = function (log) { + log._hasInitiallyLogged = true + + return trigger(log, 'command:log:added') + } + + const addToLogs = function (log) { + const id = log.get('id') + + logs[id] = true + } + + const logFn = function (options: any = {}) { + if (!_.isObject(options)) { + $errUtils.throwErrByPath('log.invalid_argument', { args: { arg: options } }) + } + + const log = Log(cy, state, config, options) + + // add event emitter interface + $Events.extend(log) + + const triggerStateChanged = () => { + return trigger(log, 'command:log:changed') + } + + // only fire the log:state:changed event + // as fast as every 4ms + // @ts-ignore + log.fireChangeEvent = _.debounce(triggerStateChanged, 4) + + log.set(options) + + // if snapshot was passed + // in, go ahead and snapshot + if (log.get('snapshot')) { + log.snapshot() + } + + // if end was passed in + // go ahead and end + if (log.get('end')) { + log.end() + } + + if (log.get('error')) { + log.error(log.get('error')) + } + + log.wrapConsoleProps() + + const onBeforeLog = state('onBeforeLog') + + // dont trigger log if this function + // explicitly returns false + if (_.isFunction(onBeforeLog)) { + if (onBeforeLog.call(cy, log) === false) { + return + } + } + + // set the log on the command + const current = state('current') + + if (current) { + current.log(log) + } + + addToLogs(log) + + if (options.sessionInfo) { + Cypress.emit('session:add', log.toJSON()) + } + + if (options.emitOnly) { + return + } + + triggerLog(log) + + // if not current state then the log is being run + // with no command reference, so just end the log + if (!current) { + log.end() + } + + return log + } + + logFn._logs = logs + + return logFn +} + export default { reduceMemory, @@ -512,124 +633,5 @@ export default { setCounter, - create (Cypress, cy, state, config) { - counter = 0 - const logs = {} - - const trigger = function (log, event) { - // bail if we never fired our initial log event - if (!log._hasInitiallyLogged) { - return - } - - // bail if we've reset the logs due to a Cypress.abort - if (!logs[log.get('id')]) { - return - } - - const attrs = log.toJSON() - - // only trigger this event if our last stored - // emitted attrs do not match the current toJSON - if (!_.isEqual(log._emittedAttrs, attrs)) { - log._emittedAttrs = attrs - - log.emit(event, attrs) - - return Cypress.action(event, attrs, log) - } - } - - const triggerLog = function (log) { - log._hasInitiallyLogged = true - - return trigger(log, 'command:log:added') - } - - const addToLogs = function (log) { - const id = log.get('id') - - logs[id] = true - } - - const logFn = function (options = {}) { - if (!_.isObject(options)) { - $errUtils.throwErrByPath('log.invalid_argument', { args: { arg: options } }) - } - - const log = Log(cy, state, config, options) - - // add event emitter interface - $Events.extend(log) - - const triggerStateChanged = () => { - return trigger(log, 'command:log:changed') - } - - // only fire the log:state:changed event - // as fast as every 4ms - log.fireChangeEvent = _.debounce(triggerStateChanged, 4) - - log.set(options) - - // if snapshot was passed - // in, go ahead and snapshot - if (log.get('snapshot')) { - log.snapshot() - } - - // if end was passed in - // go ahead and end - if (log.get('end')) { - log.end({ silent: true }) - } - - if (log.get('error')) { - log.error(log.get('error'), { silent: true }) - } - - log.wrapConsoleProps() - - const onBeforeLog = state('onBeforeLog') - - // dont trigger log if this function - // explicitly returns false - if (_.isFunction(onBeforeLog)) { - if (onBeforeLog.call(cy, log) === false) { - return - } - } - - // set the log on the command - const current = state('current') - - if (current) { - current.log(log) - } - - addToLogs(log) - - if (options.sessionInfo) { - Cypress.emit('session:add', log.toJSON()) - } - - if (options.emitOnly) { - return - } - - triggerLog(log) - - // if not current state then the log is being run - // with no command reference, so just end the log - if (!current) { - log.end({ silent: true }) - } - - return log - } - - logFn._logs = logs - - return logFn - }, + create, } diff --git a/packages/driver/src/cypress/mocha.ts b/packages/driver/src/cypress/mocha.ts index b4599cdac4..3ee403bc10 100644 --- a/packages/driver/src/cypress/mocha.ts +++ b/packages/driver/src/cypress/mocha.ts @@ -1,7 +1,6 @@ /* eslint-disable prefer-rest-params */ -// @ts-nocheck import _ from 'lodash' -import $errUtils from './error_utils' +import $errUtils, { CypressError } from './error_utils' import $utils from './utils' import $stackUtils from './stack_utils' @@ -11,7 +10,7 @@ import * as mocha from 'mocha' const { getTestFromRunnable } = $utils -const Mocha = mocha.Mocha != null ? mocha.Mocha : mocha +const Mocha = (mocha as any).Mocha != null ? (mocha as any).Mocha : mocha const { Test, Runner, Runnable, Hook, Suite } = Mocha @@ -33,8 +32,8 @@ const suiteAfterAll = Suite.prototype.afterAll const suiteAfterEach = Suite.prototype.afterEach // don't let mocha pollute the global namespace -delete window.mocha -delete window.Mocha +delete (window as any).mocha +delete (window as any).Mocha function invokeFnWithOriginalTitle (ctx, originalTitle, mochaArgs, fn, _testConfig) { const ret = fn.apply(ctx, mochaArgs) @@ -68,7 +67,7 @@ function overloadMochaFnForConfig (fnName, specWindow) { const origFn = subFn ? _fn[subFn] : _fn if (args.length > 2 && _.isObject(args[1])) { - const _testConfig = _.extend({}, args[1]) + const _testConfig = _.extend({}, args[1]) as any const mochaArgs = [args[0], args[2]] @@ -447,15 +446,18 @@ const patchSuiteHooks = (specWindow, config) => { let invocationStack = hook.invocationDetails?.stack if (!hook.invocationDetails) { - const invocationDetails = $stackUtils.getInvocationDetails(specWindow, config) + const invocationDetails = $stackUtils.getInvocationDetails(specWindow, config)! hook.invocationDetails = invocationDetails invocationStack = invocationDetails.stack } if (this._condensedHooks) { - throw $errUtils.errByPath('mocha.hook_registered_late', { hookTitle: fnName }) - .setUserInvocationStack(invocationStack) + const err = $errUtils.errByPath('mocha.hook_registered_late', { hookTitle: fnName }) as CypressError + + err.setUserInvocationStack(invocationStack) + + throw err } return hook diff --git a/packages/driver/src/cypress/runner.ts b/packages/driver/src/cypress/runner.ts index cad4c7780e..de401c631c 100644 --- a/packages/driver/src/cypress/runner.ts +++ b/packages/driver/src/cypress/runner.ts @@ -1,7 +1,4 @@ /* eslint-disable prefer-rest-params */ -/* globals Cypress */ - -// @ts-nocheck import _ from 'lodash' import dayjs from 'dayjs' import Promise from 'bluebird' @@ -28,6 +25,10 @@ const RUNNABLE_PROPS = '_testConfig id order title _titlePath root hookName hook const debug = debugFn('cypress:driver:runner') const debugErrors = debugFn('cypress:driver:errors') +const duration = (before: Date, after: Date) => { + return Number(before) - Number(after) +} + const fire = (event, runnable, Cypress) => { debug('fire: %o', { event }) if (runnable._fired == null) { @@ -103,6 +104,8 @@ const testAfterRun = (test, Cypress) => { // prevent loop comprehension return null } + + return null } const setTestTimingsForHook = (test, hookName, obj) => { @@ -126,7 +129,7 @@ const setTestTimings = (test, name, obj) => { } const setWallClockDuration = (test) => { - return test.wallClockDuration = new Date() - test.wallClockStartedAt + return test.wallClockDuration = duration(new Date(), test.wallClockStartedAt) } // we need to optimize wrap by converting @@ -137,7 +140,7 @@ const wrap = (runnable) => { return $utils.reduceProps(runnable, RUNNABLE_PROPS) } -const wrapAll = (runnable) => { +const wrapAll = (runnable): any => { return _.extend( {}, $utils.reduceProps(runnable, RUNNABLE_PROPS), @@ -201,7 +204,7 @@ const eachHookInSuite = (suite, fn) => { // iterates over a suite's tests (including nested suites) // and will return as soon as the callback is true -const findTestInSuite = (suite, fn = _.identity) => { +const findTestInSuite = (suite, fn: any = _.identity) => { for (const test of suite.tests) { if (fn(test)) { return test @@ -217,7 +220,7 @@ const findTestInSuite = (suite, fn = _.identity) => { } } -const findSuiteInSuite = (suite, fn = _.identity) => { +const findSuiteInSuite = (suite, fn: any = _.identity) => { if (fn(suite)) { return suite } @@ -240,7 +243,7 @@ const suiteHasSuite = (suite, suiteId) => { } // same as findTestInSuite but iterates backwards -const findLastTestInSuite = (suite, fn = _.identity) => { +const findLastTestInSuite = (suite, fn: any = _.identity) => { for (let i = suite.suites.length - 1; i >= 0; i--) { const test = findLastTestInSuite(suite.suites[i], fn) @@ -259,7 +262,7 @@ const findLastTestInSuite = (suite, fn = _.identity) => { } const getAllSiblingTests = (suite, getTestById) => { - const tests = [] + const tests: any[] = [] suite.eachTest((testRunnable) => { // iterate through each of our suites tests. @@ -271,6 +274,8 @@ const getAllSiblingTests = (suite, getTestById) => { if (test) { return tests.push(test) } + + return }) return tests @@ -300,7 +305,7 @@ const isLastSuite = (suite, tests) => { // grab all of the suites from our filtered tests // including all of their ancestor suites! - const suites = _.reduce(tests, (memo, test) => { + const suites = _.reduce(tests, (memo, test) => { let parent while ((parent = test.parent)) { @@ -309,8 +314,7 @@ const isLastSuite = (suite, tests) => { } return memo - } - , []) + }, []) // intersect them with our parent suites and see if the last one is us return _ @@ -357,7 +361,7 @@ const overrideRunnerHook = (Cypress, _runner, getTestById, getTest, setTest, get const test = getTest() const allTests = getTests() - let shouldFireTestAfterRun = _.noop + let shouldFireTestAfterRun = () => false switch (name) { case 'afterEach': @@ -375,6 +379,8 @@ const overrideRunnerHook = (Cypress, _runner, getTestById, getTest, setTest, get return true } } + + return false } break @@ -395,7 +401,7 @@ const overrideRunnerHook = (Cypress, _runner, getTestById, getTest, setTest, get // due to already being run on top navigation // https://github.com/cypress-io/cypress/issues/9026 if (!testIsActuallyInSuite) { - return + return false } // 1. if we're the very last test in the entire allTests @@ -410,6 +416,8 @@ const overrideRunnerHook = (Cypress, _runner, getTestById, getTest, setTest, get return true } } + + return false } break @@ -449,7 +457,7 @@ const overrideRunnerHook = (Cypress, _runner, getTestById, getTest, setTest, get const getTestResults = (tests) => { return _.map(tests, (test) => { - const obj = _.pick(test, 'id', 'duration', 'state') + const obj: Record = _.pick(test, 'id', 'duration', 'state') obj.title = test.originalTitle // TODO FIX THIS! @@ -487,7 +495,7 @@ const normalizeAll = (suite, initialTests = {}, setTestsById, setTests, onRunnab // we hand back a normalized object but also // create optimized lookups for the tests without // traversing through it multiple times - const tests = {} + const tests: Record = {} const normalizedSuite = normalize(suite, tests, initialTests, onRunnable, onLogsById, getRunnableId, getHookId, getOnlyTestId, getOnlySuiteId, createEmptyOnlyTest) if (setTestsById) { @@ -518,6 +526,8 @@ const normalizeAll = (suite, initialTests = {}, setTestsById, setTests, onRunnab } normalizedSuite.runtimeConfig[key] = v + + return }) return normalizedSuite @@ -659,6 +669,8 @@ const normalize = (runnable, tests, initialTests, onRunnable, onLogsById, getRun return normalizedChild })) } + + return null }) return normalizedRunnable @@ -736,6 +748,8 @@ const normalize = (runnable, tests, initialTests, onRunnable, onLogsById, getRun return normalizedChildSuite } + + return null })) } @@ -1016,8 +1030,8 @@ export default { create: (specWindow, mocha, Cypress, cy, state) => { let _runnableId = 0 let _hookId = 0 - let _uncaughtFn = null - let _resumedAtTestIndex = null + let _uncaughtFn: (() => never) | null = null + let _resumedAtTestIndex: number | null = null const _runner = mocha.getRunner() @@ -1098,19 +1112,19 @@ export default { specWindow.addEventListener('unhandledrejection', onSpecError('unhandledrejection')) // hold onto the _runnables for faster lookup later - let _test = null - let _tests = [] - let _testsById = {} - const _testsQueue = [] - const _testsQueueById = {} + let _test: any = null + let _tests: any[] = [] + let _testsById: Record = {} + const _testsQueue: any[] = [] + const _testsQueueById: Record = {} // only used during normalization - const _runnables = [] - const _logsById = {} + const _runnables: any[] = [] + const _logsById: Record = {} let _emissions = { started: {}, ended: {}, } - let _startTime = null + let _startTime: string | null = null let _onlyTestId = null let _onlySuiteId = null @@ -1209,7 +1223,7 @@ export default { const r = runnable const isHook = r.type === 'hook' const isTest = r.type === 'test' - const test = getTest() || getTestFromHook(runnable, getTestById) + const test = getTest() || getTestFromHook(runnable) const hookName = isHook && getHookName(r) const isBeforeEachHook = isHook && !!hookName.match(/before each/) const isAfterEachHook = isHook && !!hookName.match(/after each/) @@ -1382,11 +1396,11 @@ export default { // runtime of a runnables fn execution duration // and also the run of the runnable:after:run:async event let lifecycleStart - let wallClockEnd = null - let fnDurationStart = null - let fnDurationEnd = null - let afterFnDurationStart = null - let afterFnDurationEnd = null + let wallClockEnd: Date | null = null + let fnDurationStart: Date | null = null + let fnDurationEnd: Date | null = null + let afterFnDurationStart: Date | null = null + let afterFnDurationEnd: Date | null = null // when this is a hook, capture the real start // date so we can calculate our test's duration @@ -1432,12 +1446,12 @@ export default { // reset runnable duration to include lifecycle // and afterFn timings purely for the mocha runner. // this is what it 'feels' like to the user - runnable.duration = wallClockEnd - wallClockStartedAt + runnable.duration = duration(wallClockEnd, wallClockStartedAt) setTestTimingsForHook(test, hookName, { hookId: runnable.hookId, - fnDuration: fnDurationEnd - fnDurationStart, - afterFnDuration: afterFnDurationEnd - afterFnDurationStart, + fnDuration: duration(fnDurationEnd!, fnDurationStart!), + afterFnDuration: duration(afterFnDurationEnd, afterFnDurationStart!), }) break @@ -1446,13 +1460,13 @@ export default { // if we are currently on a test then // recalculate its duration to be based // against that (purely for the mocha reporter) - test.duration = wallClockEnd - test.wallClockStartedAt + test.duration = duration(wallClockEnd, test.wallClockStartedAt) // but still preserve its actual function // body duration for timings setTestTimings(test, 'test', { - fnDuration: fnDurationEnd - fnDurationStart, - afterFnDuration: afterFnDurationEnd - afterFnDurationStart, + fnDuration: duration(fnDurationEnd!, fnDurationStart!), + afterFnDuration: duration(afterFnDurationEnd!, afterFnDurationStart!), }) break @@ -1558,7 +1572,7 @@ export default { if (lifecycleStart) { // capture how long the lifecycle took as part // of the overall wallClockDuration of our test - setTestTimings(test, 'lifecycle', new Date() - lifecycleStart) + setTestTimings(test, 'lifecycle', duration(new Date(), lifecycleStart)) } // capture the moment we're about to invoke @@ -1664,9 +1678,11 @@ export default { if (attrs) { return $Log.getSnapshotProps(attrs) } + + return }, - resumeAtTest (id, emissions = {}) { + resumeAtTest (id, emissions: any = {}) { _resumedAtTestIndex = getTestIndexFromId(id) _emissions = emissions diff --git a/packages/driver/src/cypress/screenshot.ts b/packages/driver/src/cypress/screenshot.ts index 43dfa207d1..d9648a232d 100644 --- a/packages/driver/src/cypress/screenshot.ts +++ b/packages/driver/src/cypress/screenshot.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import _ from 'lodash' import $utils from './utils' @@ -13,8 +11,8 @@ const _reset = () => { screenshotOnRunFailure: true, blackout: [], overwrite: false, - onBeforeScreenshot () {}, - onAfterScreenshot () {}, + onBeforeScreenshot ($el) {}, + onAfterScreenshot ($el, results) {}, } } @@ -111,8 +109,8 @@ const validateAndSetCallback = (props, values, cmd, log, option) => { values[option] = value } -const validate = (props, cmd, log) => { - const values = {} +const validate = (props, cmd, log?) => { + const values: Record = {} if (!_.isPlainObject(props)) { $errUtils.throwErrByPath('screenshot.invalid_arg', { diff --git a/packages/driver/src/cypress/script_utils.ts b/packages/driver/src/cypress/script_utils.ts index f953723200..7af8b90d8a 100644 --- a/packages/driver/src/cypress/script_utils.ts +++ b/packages/driver/src/cypress/script_utils.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import _ from 'lodash' import Bluebird from 'bluebird' @@ -14,7 +12,7 @@ const fetchScript = (scriptWindow, script) => { } const extractSourceMap = ([script, contents]) => { - script.fullyQualifiedUrl = `${window.top.location.origin}${script.relativeUrl}`.replace(/ /g, '%20') + script.fullyQualifiedUrl = `${window.top!.location.origin}${script.relativeUrl}`.replace(/ /g, '%20') const sourceMap = $sourceMapUtils.extractSourceMap(script, contents) @@ -22,7 +20,7 @@ const extractSourceMap = ([script, contents]) => { .return([script, contents]) } -const evalScripts = (specWindow, scripts = []) => { +const evalScripts = (specWindow, scripts: any = []) => { _.each(scripts, ([script, contents]) => { specWindow.eval(`${contents}\n//# sourceURL=${script.fullyQualifiedUrl}`) }) @@ -32,7 +30,7 @@ const evalScripts = (specWindow, scripts = []) => { const runScriptsFromUrls = (specWindow, scripts) => { return Bluebird - .map(scripts, (script) => fetchScript(specWindow, script)) + .map(scripts, (script) => fetchScript(specWindow, script)) .map(extractSourceMap) .then((scripts) => evalScripts(specWindow, scripts)) } @@ -46,7 +44,7 @@ export default { // NOTE: since in evalScripts, scripts are evaluated in order, // we chose to respect this constraint here too. // indeed _.each goes through the array in order - return Bluebird.each(scripts, (script) => script()) + return Bluebird.each(scripts, (script: any) => script()) } return runScriptsFromUrls(specWindow, scripts) diff --git a/packages/driver/src/cypress/source_map_utils.ts b/packages/driver/src/cypress/source_map_utils.ts index 88b0eb965c..24adc14faf 100644 --- a/packages/driver/src/cypress/source_map_utils.ts +++ b/packages/driver/src/cypress/source_map_utils.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import _ from 'lodash' import { SourceMapConsumer } from 'source-map' import Promise from 'bluebird' @@ -16,6 +15,7 @@ let sourceMapConsumers = {} const initializeSourceMapConsumer = (file, sourceMap) => { if (!sourceMap) return Promise.resolve(null) + // @ts-ignore SourceMapConsumer.initialize({ 'lib/mappings.wasm': mappingsWasm, }) @@ -32,7 +32,7 @@ const extractSourceMap = (file, fileContents) => { if (!sourceMapMatch) return null - const url = _.last(sourceMapMatch) + const url = _.last(sourceMapMatch) as any const dataUrlMatch = url.match(regexDataUrl) if (!dataUrlMatch) return null diff --git a/packages/driver/src/cypress/stack_utils.ts b/packages/driver/src/cypress/stack_utils.ts index 34c8e90fa9..674bf67bb9 100644 --- a/packages/driver/src/cypress/stack_utils.ts +++ b/packages/driver/src/cypress/stack_utils.ts @@ -1,5 +1,4 @@ // See: ./errorScenarios.md for details about error messages and stack traces -// @ts-nocheck import _ from 'lodash' import path from 'path' import errorStackParser from 'error-stack-parser' @@ -52,7 +51,7 @@ const stackWithLinesRemoved = (stack, cb) => { const stackWithLinesDroppedFromMarker = (stack, marker, includeLast = false) => { return stackWithLinesRemoved(stack, (lines) => { // drop lines above the marker - const withAboveMarkerRemoved = _.dropWhile(lines, (line) => { + const withAboveMarkerRemoved = _.dropWhile(lines, (line: any) => { return !_.includes(line, marker) }) @@ -96,6 +95,8 @@ const stackWithUserInvocationStackSpliced = (err, userInvocationStack): StackAnd } } +type InvocationDetails = LineDetail | {} + const getInvocationDetails = (specWindow, config) => { if (specWindow.Error) { let stack = (new specWindow.Error()).stack @@ -110,12 +111,14 @@ const getInvocationDetails = (specWindow, config) => { stack = stackWithLinesDroppedFromMarker(stack, '__cypress/tests', true) } - const details = getSourceDetailsForFirstLine(stack, config('projectRoot')) || {} + const details: InvocationDetails = getSourceDetailsForFirstLine(stack, config('projectRoot')) || {}; - details.stack = stack + (details as any).stack = stack - return details + return details as (InvocationDetails & { stack: any }) } + + return } const getLanguageFromExtension = (filePath) => { @@ -240,7 +243,7 @@ const parseLine = (line) => { if (!isStackLine) return - const parsed = errorStackParser.parse({ stack: line })[0] + const parsed = errorStackParser.parse({ stack: line } as any)[0] if (!parsed) return @@ -270,7 +273,23 @@ const stripCustomProtocol = (filePath) => { return filePath.replace(customProtocolRegex, '') } -const getSourceDetailsForLine = (projectRoot, line) => { +type LineDetail = +{ + message: any + whitespace: any +} | +{ + function: any + fileUrl: any + originalFile: any + relativeFile: any + absoluteFile: any + line: any + column: number + whitespace: any +} + +const getSourceDetailsForLine = (projectRoot, line): LineDetail => { const whitespace = getWhitespace(line) const generatedDetails = parseLine(line) @@ -325,7 +344,7 @@ const reconstructStack = (parsedStack) => { }).join('\n') } -const getSourceStack = (stack, projectRoot) => { +const getSourceStack = (stack, projectRoot?) => { if (!_.isString(stack)) return {} const getSourceDetailsWithStackUtil = _.partial(getSourceDetailsForLine, projectRoot) diff --git a/packages/driver/src/cypress/utils.ts b/packages/driver/src/cypress/utils.ts index 9b83e5c82f..3783077187 100644 --- a/packages/driver/src/cypress/utils.ts +++ b/packages/driver/src/cypress/utils.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import _ from 'lodash' import capitalize from 'underscore.string/capitalize' import methods from 'methods' @@ -46,7 +45,7 @@ const USER_FRIENDLY_TYPE_DETECTORS = _.map([ [_.stubTrue, 'unknown'], ], ([fn, type]) => { return [fn, _.constant(type)] -}) +}) as [(val: any) => boolean, (val: Function) => Function][] export default { warning (msg) { @@ -78,7 +77,7 @@ export default { const item = [].concat(val)[0] if ($jquery.isJquery(item)) { - return item.first() + return (item as JQuery).first() } return item @@ -154,7 +153,7 @@ export default { memo.push(`${`${key}`.toLowerCase()}: ${this.stringifyActual(value)}`) return memo - }, []) + }, [] as string[]) return `{${str.join(', ')}}` }, @@ -185,7 +184,7 @@ export default { if (_.isObject(value)) { // Cannot use $dom.isJquery here because it causes infinite recursion. if (value instanceof $) { - return `jQuery{${value.length}}` + return `jQuery{${(value as JQueryStatic).length}}` } const len = _.keys(value).length @@ -396,7 +395,7 @@ export default { */ encodeBase64Unicode (str) { return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (match, p1) => { - return String.fromCharCode(`0x${p1}`) + return String.fromCharCode(Number(`0x${p1}`)) })) }, diff --git a/packages/driver/src/cypress/xml_http_request.ts b/packages/driver/src/cypress/xml_http_request.ts index 56e3d8b315..133c433f99 100644 --- a/packages/driver/src/cypress/xml_http_request.ts +++ b/packages/driver/src/cypress/xml_http_request.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import $errUtils from './error_utils' const isCypressHeaderRe = /^X-Cypress-/i @@ -15,6 +13,16 @@ const parseJSON = (text) => { // maybe rename this to XMLHttpRequest ? // so it shows up correctly as an instance in the console class XMLHttpRequest { + xhr: any + id: any + url: any + method: any + status: any + statusMessage: any + request: any + response: any + duration: any + constructor (xhr) { this.xhr = xhr this.id = this.xhr.id diff --git a/packages/driver/src/dom/jquery.ts b/packages/driver/src/dom/jquery.ts index ce4da8fd3c..e39b836672 100644 --- a/packages/driver/src/dom/jquery.ts +++ b/packages/driver/src/dom/jquery.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import $ from 'jquery' import _ from 'lodash' @@ -8,6 +7,7 @@ const wrap = (obj) => { } const query = (selector, context) => { + // @ts-ignore return new $.fn.init(selector, context) } diff --git a/packages/driver/src/dom/window.ts b/packages/driver/src/dom/window.ts index dcdad4f2ae..218b5f7738 100644 --- a/packages/driver/src/dom/window.ts +++ b/packages/driver/src/dom/window.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import $jquery from './jquery' import $document from './document' diff --git a/packages/driver/src/util/limited_map.ts b/packages/driver/src/util/limited_map.ts index 421fefba1c..986c93f3fa 100644 --- a/packages/driver/src/util/limited_map.ts +++ b/packages/driver/src/util/limited_map.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import _ from 'lodash' // IE doesn't support Array.from or Map.prototype.keys @@ -8,7 +6,7 @@ const getMapKeys = (map) => { return Array.from(map.keys()) } - const keys = [] + const keys: any[] = [] map.forEach((key) => { keys.push(key) @@ -18,6 +16,8 @@ const getMapKeys = (map) => { } class LimitedMap extends Map { + private _limit: number + constructor (limit = 100) { super() diff --git a/packages/driver/types/internal-types.d.ts b/packages/driver/types/internal-types.d.ts index cef1ff6550..fccf8dbd39 100644 --- a/packages/driver/types/internal-types.d.ts +++ b/packages/driver/types/internal-types.d.ts @@ -36,6 +36,8 @@ declare namespace Cypress { sinon: sinon.SinonApi utils: CypressUtils state: State + + originalConfig: Record } interface CypressUtils { diff --git a/packages/errors/src/stackUtils.ts b/packages/errors/src/stackUtils.ts index 78017b1857..7e95ce96cb 100644 --- a/packages/errors/src/stackUtils.ts +++ b/packages/errors/src/stackUtils.ts @@ -21,7 +21,7 @@ export const splitStack = (stack: string) => { }, [[], []] as MessageLines) } -export const unsplitStack = (messageLines: string, stackLines: string[]) => { +export const unsplitStack = (messageLines: string | string[], stackLines: string[]) => { return _.castArray(messageLines).concat(stackLines).join('\n') } From ffd3627e4359c405770934d16c0f5bfef6c9c6b8 Mon Sep 17 00:00:00 2001 From: Zach Bloomquist Date: Fri, 25 Feb 2022 18:12:05 -0500 Subject: [PATCH 8/8] test: migrate module_api to system tests (#20265) Co-authored-by: Ryan Manuel --- __snapshots__/bump-spec.js | 15 - circle.yml | 53 -- cli/package.json | 2 - guides/release-process.md | 9 +- guides/testing-other-projects.md | 32 +- package.json | 4 - scripts/binary/bump.js | 268 ------ scripts/binary/index.js | 18 - scripts/test-other-projects.js | 139 --- scripts/unit/binary/bump-spec.js | 48 -- system-tests/lib/docker.ts | 8 +- system-tests/projects/module-api/.gitignore | 12 + system-tests/projects/module-api/README.md | 5 + system-tests/projects/module-api/async.js | 15 + system-tests/projects/module-api/cypress.json | 4 + .../module-api/cypress/integration/a-spec.js | 5 + .../module-api/cypress/integration/b-spec.js | 5 + system-tests/projects/module-api/index.js | 20 + system-tests/projects/module-api/package.json | 22 + .../projects/module-api/test/env/cypress.json | 4 + .../test/env/cypress/integration/env-spec.js | 11 + .../test/failing/__snapshots__/spec.js | 15 + .../module-api/test/failing/cypress.json | 4 + .../failing/cypress/integration/a-spec.js | 5 + .../failing/cypress/integration/b-spec.js | 5 + .../test/invalid/__snapshots__/spec.js | 15 + .../module-api/test/invalid/cypress.json | 4 + .../invalid/cypress/integration/a-spec.js | 4 + .../invalid/cypress/integration/b-spec.js | 5 + system-tests/projects/module-api/test/spec.js | 158 ++++ .../test/successful/__snapshots__/spec.js | 15 + .../module-api/test/successful/cypress.json | 4 + .../successful/cypress/integration/a-spec.js | 5 + .../successful/cypress/integration/b-spec.js | 5 + system-tests/projects/module-api/yarn.lock | 815 ++++++++++++++++++ .../scripts/bootstrap-docker-container.sh | 4 +- system-tests/test-binary/module_api_spec.ts | 13 + yarn.lock | 86 +- 38 files changed, 1206 insertions(+), 655 deletions(-) delete mode 100644 __snapshots__/bump-spec.js delete mode 100644 scripts/binary/bump.js delete mode 100644 scripts/test-other-projects.js delete mode 100644 scripts/unit/binary/bump-spec.js create mode 100644 system-tests/projects/module-api/.gitignore create mode 100644 system-tests/projects/module-api/README.md create mode 100644 system-tests/projects/module-api/async.js create mode 100644 system-tests/projects/module-api/cypress.json create mode 100644 system-tests/projects/module-api/cypress/integration/a-spec.js create mode 100644 system-tests/projects/module-api/cypress/integration/b-spec.js create mode 100644 system-tests/projects/module-api/index.js create mode 100644 system-tests/projects/module-api/package.json create mode 100644 system-tests/projects/module-api/test/env/cypress.json create mode 100644 system-tests/projects/module-api/test/env/cypress/integration/env-spec.js create mode 100644 system-tests/projects/module-api/test/failing/__snapshots__/spec.js create mode 100644 system-tests/projects/module-api/test/failing/cypress.json create mode 100644 system-tests/projects/module-api/test/failing/cypress/integration/a-spec.js create mode 100644 system-tests/projects/module-api/test/failing/cypress/integration/b-spec.js create mode 100644 system-tests/projects/module-api/test/invalid/__snapshots__/spec.js create mode 100644 system-tests/projects/module-api/test/invalid/cypress.json create mode 100644 system-tests/projects/module-api/test/invalid/cypress/integration/a-spec.js create mode 100644 system-tests/projects/module-api/test/invalid/cypress/integration/b-spec.js create mode 100644 system-tests/projects/module-api/test/spec.js create mode 100644 system-tests/projects/module-api/test/successful/__snapshots__/spec.js create mode 100644 system-tests/projects/module-api/test/successful/cypress.json create mode 100644 system-tests/projects/module-api/test/successful/cypress/integration/a-spec.js create mode 100644 system-tests/projects/module-api/test/successful/cypress/integration/b-spec.js create mode 100644 system-tests/projects/module-api/yarn.lock create mode 100644 system-tests/test-binary/module_api_spec.ts diff --git a/__snapshots__/bump-spec.js b/__snapshots__/bump-spec.js deleted file mode 100644 index 73a99a47b3..0000000000 --- a/__snapshots__/bump-spec.js +++ /dev/null @@ -1,15 +0,0 @@ -exports['list of all projects'] = [ - { - "repo": "cypress-io/cypress-test-module-api", - "provider": "circle", - "platform": "linux" - } -] - -exports['should have just circle and linux projects'] = [ - { - "repo": "cypress-io/cypress-test-module-api", - "provider": "circle", - "platform": "linux" - } -] diff --git a/circle.yml b/circle.yml index eb597e8a69..4d2dd83510 100644 --- a/circle.yml +++ b/circle.yml @@ -1705,39 +1705,6 @@ jobs: yarn cypress:run --project /tmp/cypress-test-tiny --record - store-npm-logs - test-binary-and-npm-against-other-projects: - <<: *defaults - parameters: - <<: *defaultsParameters - resource_class: - type: string - default: medium - resource_class: << parameters.resource_class >> - steps: - # needs uploaded NPM and test binary - - restore_cached_workspace - - run: ls -la - # make sure JSON files with uploaded urls are present - - run: ls -la binary-url.json npm-package-url.json - - run: cat binary-url.json - - run: cat npm-package-url.json - - run: - # install NPM from unique urls - name: Install Cypress Binary in Dummy Package - command: | - node scripts/test-unique-npm-and-binary.js \ - --npm npm-package-url.json \ - --binary binary-url.json \ - --cwd /tmp/testing - - run: - name: Running other test projects with new NPM package and binary - command: | - node scripts/test-other-projects.js \ - --npm npm-package-url.json \ - --binary binary-url.json \ - --provider circle - - store-npm-logs - test-npm-module-and-verify-binary: <<: *defaults steps: @@ -2278,11 +2245,6 @@ linux-workflow: &linux-workflow - test-binary-against-kitchensink: requires: - create-build-artifacts - - test-binary-and-npm-against-other-projects: - context: test-runner:trigger-test-jobs - <<: *mainBuildFilters - requires: - - create-build-artifacts - test-npm-module-and-verify-binary: <<: *mainBuildFilters requires: @@ -2373,13 +2335,6 @@ mac-workflow: &mac-workflow requires: - darwin-build - - test-binary-and-npm-against-other-projects: - context: test-runner:trigger-test-jobs - name: darwin-test-binary-and-npm-against-other-projects - executor: mac - requires: - - darwin-create-build-artifacts - windows-workflow: &windows-workflow jobs: - node_modules_install: @@ -2419,14 +2374,6 @@ windows-workflow: &windows-workflow requires: - windows-build - - test-binary-and-npm-against-other-projects: - context: test-runner:trigger-test-jobs - name: windows-test-binary-and-npm-against-other-projects - executor: windows - resource_class: windows.medium - requires: - - windows-create-build-artifacts - workflows: linux: <<: *linux-workflow diff --git a/cli/package.json b/cli/package.json index 68c2cc06eb..b068831e47 100644 --- a/cli/package.json +++ b/cli/package.json @@ -9,8 +9,6 @@ "build": "node ./scripts/build.js", "dtslint": "dtslint types", "postinstall": "node ./scripts/post-install.js", - "prerelease": "yarn build", - "release": "cd build && releaser --no-node --no-changelog", "size": "t=\"cypress-v0.0.0.tgz\"; yarn pack --filename \"${t}\"; wc -c \"${t}\"; tar tvf \"${t}\"; rm \"${t}\";", "test": "yarn test-unit", "test-debug": "node --inspect-brk $(yarn bin mocha)", diff --git a/guides/release-process.md b/guides/release-process.md index 44a8b0c1b6..ad2d8ba94c 100644 --- a/guides/release-process.md +++ b/guides/release-process.md @@ -111,14 +111,7 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy - Go into a project, run a quick test, make sure things look right - Install the new version into an established project and run the tests there - [cypress-realworld-app](https://github.com/cypress-io/cypress-realworld-app) uses yarn and represents a typical consumer implementation. - - Optionally, do more thorough tests: - - Trigger test projects from the command line (if you have the appropriate permissions) - - ```shell - node scripts/test-other-projects.js --npm cypress@X.Y.Z --binary X.Y.Z - ``` - - - Test the new version of Cypress against the Cypress dashboard repo. + - Optionally, do more thorough tests, for example test the new version of Cypress against the Cypress dashboard repo. 7. Confirm that every issue labeled [stage: pending release](https://github.com/cypress-io/cypress/issues?q=label%3A%22stage%3A+pending+release%22+is%3Aclosed) has a ZenHub release set. **Tip:** there is a command in [`release-automations`](https://github.com/cypress-io/release-automations)'s `issues-in-release` tool to list and check such issues. Without a ZenHub release issues will not be included in the right changelog. diff --git a/guides/testing-other-projects.md b/guides/testing-other-projects.md index 16e1f34246..41f3516eaa 100644 --- a/guides/testing-other-projects.md +++ b/guides/testing-other-projects.md @@ -4,10 +4,10 @@ In `develop`, `master`, and any other branch configured in [`circle.yml`](../cir Two main strategies are used to spawn these test projects: -1. Local CI +1. `test-binary-against-repo` jobs 2. Remote CI -## Local CI +## `test-binary-against-repo` jobs A number of CI jobs in `circle.yml` clone test projects and run tests as part of `cypress-io/cypress`'s CI pipeline. @@ -17,30 +17,6 @@ Similarly to "Remote CI" test projects, Local CI test projects will attempt to c One advantage to local CI is that it does not require creating commits to another repo. -## Remote CI +## `binary-system-tests` -After the production binary and NPM package are build and uploaded in CI, [`/scripts/test-other-projects.js`](../scripts/test-other-projects.js) is run as part of the `test-other-projects` `circle.yml` step. - -This script creates commits inside of several test projects (hence "Remote CI") in order to trigger a realistic, continous-integration test of Cypress. - -For a list of the projects, see the definition of `_PROVIDERS` in [`/scripts/binary/bump.js`](../scripts/binary/bump.js). - -For each project and operating system combo in `_PROVIDERS`, the script: - -1. Creates a commit to the test project's GitHub repo using the API. [An example of such a commit.](https://github.com/cypress-io/cypress-test-tiny/commit/5b39f3f43f6b7598f0d57cffcba71a7048d1d809) - * Note the commit is specifically for `linux`, and only the `linux-tests` job runs to completion. - * If a branch exists that is named after the [next version](./next-version.md) (`X.Y.Z`), the commit will be made to that branch. - * This is useful to test a release's breaking changes or new features against an example project without having to have the project's main branch in a broken state. - * Otherwise, the default branch is used for the commit. -2. Creates a status check in this GitHub repo (`cypress-io/cypress`) and marks it `pending`. -3. Waits for the test project's CI workflow to finish running. - * Each test project is configured to use [`@cypress/commit-message-install`](https://github.com/cypress-io/commit-message-install) to configure the exact test required via the information in the commit message. - * Each test project is configured to update the `pending` CI job in `cypress-io/cypress` to a `success` when the CI workflow successfully finishes. - -These tests add coverage to the Cypress code base by: - -* Providing a super-close-to-real-world usage of Cypress (i.e. installing fresh from an NPM package and running in a bare repo using the repo's CI setup) -* Testing in a variety of environments - * Different Node.js versions - * Different operating systems - * A multitude of CI providers \ No newline at end of file +System tests in `/system-tests/test-binary` are run against the built Cypress App in CI. For more details, see the [README](../system-tests/README.md). \ No newline at end of file diff --git a/package.json b/package.json index 9dfb4ed2b5..9473ff3ffe 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ "binary-zip": "node ./scripts/binary.js zip", "build": "lerna run build --stream --ignore create-cypress-tests && lerna run build --stream --scope create-cypress-tests", "build-prod": "lerna run build-prod --stream --ignore create-cypress-tests && lerna run build-prod --stream --scope create-cypress-tests", - "bump": "node ./scripts/binary.js bump", "check-node-version": "node scripts/check-node-version.js", "check-terminal": "node scripts/check-terminal.js", "clean": "lerna run clean --parallel --no-bail || echo 'ok, errors while cleaning'", @@ -73,7 +72,6 @@ "nvm": "0.0.4" }, "devDependencies": { - "@cypress/bumpercar": "2.0.12", "@cypress/commit-message-install": "3.1.3", "@cypress/env-or-json-file": "2.0.0", "@cypress/github-commit-status-check": "1.5.0", @@ -157,14 +155,12 @@ "lint-staged": "11.1.2", "listr2": "3.8.3", "lodash": "^4.17.21", - "make-empty-github-commit": "cypress-io/make-empty-github-commit#4a592aedb776ba2f4cc88979055315a53eec42ee", "minimist": "1.2.5", "mocha": "3.5.3", "mocha-banner": "1.1.2", "mocha-junit-reporter": "2.0.0", "mocha-multi-reporters": "1.1.7", "mock-fs": "5.1.1", - "parse-github-repo-url": "1.4.1", "patch-package": "6.4.7", "plist": "3.0.4", "pluralize": "8.0.0", diff --git a/scripts/binary/bump.js b/scripts/binary/bump.js deleted file mode 100644 index f368f9aadc..0000000000 --- a/scripts/binary/bump.js +++ /dev/null @@ -1,268 +0,0 @@ -const _ = require('lodash') -const Promise = require('bluebird') -const bumpercar = require('@cypress/bumpercar') -const path = require('path') -const la = require('lazy-ass') -const check = require('check-more-types') -const { configFromEnvOrJsonFile, filenameToShellVariable } = require('@cypress/env-or-json-file') -const makeEmptyGithubCommit = require('make-empty-github-commit') -const parse = require('parse-github-repo-url') -const { setCommitStatus } = require('@cypress/github-commit-status-check') - -let car = null - -// all the projects to trigger / run / change environment variables for -const _PROVIDERS = { - circle: { - main: 'cypress-io/cypress', - linux: [ - 'cypress-io/cypress-test-module-api', - ], - }, -} - -const remapProjects = function (projectsByProvider) { - const list = [] - - _.mapValues(projectsByProvider, (provider, name) => { - const remapPlatform = (platform, repos) => { - return repos.forEach((repo) => { - return list.push({ - repo, - provider: name, - platform, - }) - }) - } - - if (provider.win32) { - remapPlatform('win32', provider.win32) - } - - if (provider.linux) { - remapPlatform('linux', provider.linux) - } - - if (provider.darwin) { - return remapPlatform('darwin', provider.darwin) - } - }) - - return list -} - -// make flat list of objects -// {repo, provider, platform} -const PROJECTS = remapProjects(_PROVIDERS) - -const getCiConfig = function () { - const key = path.join('scripts', 'support', 'ci.json') - const config = configFromEnvOrJsonFile(key) - - if (!config) { - console.error('⛔️ Cannot find CI credentials') - console.error('Using @cypress/env-or-json-file module') - console.error('and filename', key) - console.error('which is environment variable', filenameToShellVariable(key)) - throw new Error('CI config not found') - } - - return config -} - -const awaitEachProjectAndProvider = function (projects, fn, filter = (val) => val) { - const creds = getCiConfig() - - // configure a new Bumpercar - const providers = {} - - if (check.unemptyString(creds.githubToken)) { - providers.travis = { - githubToken: process.env.GH_TOKEN, - } - } - - if (check.unemptyString(creds.circleToken)) { - providers.circle = { - circleToken: creds.circleToken, - } - } - - const providerNames = Object.keys(providers) - - console.log('configured providers', providerNames) - la(check.not.empty(providerNames), 'empty list of providers') - - car = bumpercar.create({ providers }) - - const filteredProjects = projects.filter(filter) - - if (check.empty(filteredProjects)) { - console.log('⚠️ zero filtered projects left after filtering') - } - - console.log('filtered projects:') - console.table(filteredProjects) - - return Promise.mapSeries(filteredProjects, (project) => { - return fn(project.repo, project.provider, creds) - }) -} - -// do not trigger all projects if there is specific provider -const getFilterByProvider = function (providerName, platformName) { - return (val) => { - if (providerName && val.provider !== providerName) { - return false - } - - if (platformName && val.platform !== platformName) { - return false - } - - return val - } -} - -module.exports = { - _PROVIDERS, - - remapProjects, - - getFilterByProvider, - - // in each project, set a couple of environment variables - version (nameOrUrl, binaryVersionOrUrl, platform, providerName) { - console.log('All possible projects:') - console.table(PROJECTS) - - la(check.unemptyString(nameOrUrl), - 'missing cypress name or url to set', nameOrUrl) - - if (check.semver(nameOrUrl)) { - console.log('for version', nameOrUrl) - nameOrUrl = `cypress@${nameOrUrl}` - console.log('full NPM install name is', nameOrUrl) - } - - la(check.unemptyString(binaryVersionOrUrl), - 'missing binary version or url', binaryVersionOrUrl) - - const result = { - versionName: nameOrUrl, - binary: binaryVersionOrUrl, - } - - const projectFilter = getFilterByProvider(providerName) - - const updateProject = function (project, provider) { - console.log('setting environment variables in', project) - - return car.updateProjectEnv(project, provider, { - CYPRESS_NPM_PACKAGE_NAME: nameOrUrl, - CYPRESS_INSTALL_BINARY: binaryVersionOrUrl, - }) - } - - return awaitEachProjectAndProvider(PROJECTS, updateProject, projectFilter) - .then(() => result) - }, - - // triggers test projects on multiple CIs - // the test projects will exercise the new version of - // the Cypress test runner we just built - runTestProjects (getStatusAndMessage, providerName, version, platform) { - const projectFilter = getFilterByProvider(providerName, platform) - - const makeCommit = function (project, provider, creds) { - // make empty commit to trigger CIs - // project is owner/repo string like cypress-io/cypress-test-tiny - console.log('making commit to project', project) - - // print if we have a few github variables present - console.log('do we have GH_APP_ID?', Boolean(process.env.GH_APP_ID)) - console.log('do we have GH_INSTALLATION_ID?', Boolean(process.env.GH_INSTALLATION_ID)) - console.log('do we have GH_PRIVATE_KEY?', Boolean(process.env.GH_PRIVATE_KEY)) - console.log('do we have GH_TOKEN?', Boolean(process.env.GH_TOKEN)) - - const parsedRepo = parse(project) - const owner = parsedRepo[0] - const repo = parsedRepo[1] - - let { status, message } = getStatusAndMessage(repo) - - if (!message) { - message = - `\ -Testing new Cypress version ${version} -\ -` - - if (process.env.CIRCLE_BUILD_URL) { - message += '\n' - message += `Circle CI build url ${process.env.CIRCLE_BUILD_URL}` - } - } - - const defaultOptions = { - owner, - repo, - message, - token: process.env.GH_TOKEN, - } - - const createGithubCommitStatusCheck = function ({ sha }) { - if (!status) { - return - } - - // status is {owner, repo, sha} and maybe a few other properties - const isStatus = check.schema({ - owner: check.unemptyString, - repo: check.unemptyString, - sha: check.commitId, - context: check.unemptyString, - platform: check.unemptyString, - arch: check.unemptyString, - }) - - if (!isStatus(status)) { - console.error('Invalid status object %o', status) - } - - const targetUrl = `https://github.com/${owner}/${repo}/commit/${sha}` - const commitStatusOptions = { - targetUrl, - owner: status.owner, - repo: status.repo, - sha: status.sha, - context: status.context, - state: 'pending', - description: `${owner}/${repo}`, - } - - console.log( - 'creating commit status check', - commitStatusOptions.description, - commitStatusOptions.context, - ) - - return setCommitStatus(commitStatusOptions) - } - - if (!version) { - return makeEmptyGithubCommit(defaultOptions).then(createGithubCommitStatusCheck) - } - - // first try to commit to branch for next upcoming version - return makeEmptyGithubCommit({ ...defaultOptions, branch: version }) - .catch(() => { - // maybe there is no branch for next version - // try default branch - return makeEmptyGithubCommit(defaultOptions) - }).then(createGithubCommitStatusCheck) - } - - return awaitEachProjectAndProvider(PROJECTS, makeCommit, projectFilter) - }, -} diff --git a/scripts/binary/index.js b/scripts/binary/index.js index 7e15711670..5319866c18 100644 --- a/scripts/binary/index.js +++ b/scripts/binary/index.js @@ -16,7 +16,6 @@ const rp = require('@cypress/request-promise') const zip = require('./zip') const ask = require('./ask') -const bump = require('./bump') const meta = require('./meta') const build = require('./build') const upload = require('./upload') @@ -104,23 +103,6 @@ const deploy = { return opts }, - bump () { - return ask.whichBumpTask() - .then((task) => { - switch (task) { - case 'run': - return bump.runTestProjects() - case 'version': - return ask.whichVersion(meta.distDir('')) - .then((v) => { - return bump.version(v) - }) - default: - throw new Error('unknown task') - } - }) - }, - release () { // read off the argv const options = this.parseOptions(process.argv) diff --git a/scripts/test-other-projects.js b/scripts/test-other-projects.js deleted file mode 100644 index c93a81cbdc..0000000000 --- a/scripts/test-other-projects.js +++ /dev/null @@ -1,139 +0,0 @@ -const la = require('lazy-ass') -const is = require('check-more-types') -const { getNameAndBinary, getJustVersion, getShortCommit } = require('./utils') -const bump = require('./binary/bump') -const { stripIndent } = require('common-tags') -const os = require('os') -const minimist = require('minimist') -const { getInstallJson } = require('@cypress/commit-message-install') - -/* eslint-disable no-console */ - -// See ../guides/testing-other-projects.md for documentation. - -const { npm, binary } = getNameAndBinary(process.argv) - -la(is.unemptyString(npm), 'missing npm url') -la(is.unemptyString(binary), 'missing binary url') -const platform = os.platform() -const arch = os.arch() - -console.log('bumping versions for other projects') -console.log(' npm:', npm) -console.log(' binary:', binary) -console.log(' platform:', platform) -console.log(' arch:', arch) - -const cliOptions = minimist(process.argv, { - string: 'provider', - alias: { - provider: 'p', - }, -}) - -/** - * Returns given string surrounded by ```json + ``` quotes - * @param {string} s - */ -const toJsonCodeBlock = (s) => { - const start = '```json' - const finish = '```' - - return `${start}\n${s}\n${finish}\n` -} - -/** - * Converts given JSON object into markdown text block - * @param {object} object - */ -const toMarkdownJsonBlock = (object) => { - la(object, 'expected an object to convert to JSON', object) - const str = JSON.stringify(object, null, 2) - - return toJsonCodeBlock(str) -} - -console.log('starting each test projects') - -const shortNpmVersion = getJustVersion(npm) - -console.log('short NPM version', shortNpmVersion) - -let subject = `Testing new ${platform} ${arch} Cypress version ${shortNpmVersion}` -const commitInfo = getShortCommit() - -if (commitInfo) { - subject += ` ${commitInfo.short}` -} - -// instructions for installing this binary, -// see "@cypress/commit-message-install" -const env = { - CYPRESS_INSTALL_BINARY: binary, -} - -const getStatusAndMessage = (projectRepoName) => { - // also pass "status" object that points back at this repo and this commit - // so that other projects can report their test success as GitHub commit status check - let status = null - const commit = commitInfo && commitInfo.sha - - if (commit && is.commitId(commit)) { - // commit is full 40 character hex string - const platform = os.platform() - const arch = os.arch() - - status = { - owner: 'cypress-io', - repo: 'cypress', - sha: commit, - platform, - arch, - context: `[${platform}-${arch}] ${projectRepoName}`, - } - } - - const commitMessageInstructions = getInstallJson({ - packages: npm, - env, - platform, - arch, - branch: shortNpmVersion, // use as version as branch name on test projects - commit, - status, - }) - const jsonBlock = toMarkdownJsonBlock(commitMessageInstructions) - const footer = - 'Use tool `@cypress/commit-message-install` to install from above block' - let message = `${subject}\n\n${jsonBlock}\n${footer}\n` - - if (process.env.CIRCLE_BUILD_URL) { - message += '\n' - message += stripIndent` - CircleCI job url: ${process.env.CIRCLE_BUILD_URL} - ` - } - - console.log('commit message:') - console.log(message) - - return { - status, - message, - } -} - -const onError = (e) => { - console.error('could not bump test projects') - console.error(e) - process.exit(1) -} - -bump -.runTestProjects( - getStatusAndMessage, - cliOptions.provider, - shortNpmVersion, - platform, -) -.catch(onError) diff --git a/scripts/unit/binary/bump-spec.js b/scripts/unit/binary/bump-spec.js deleted file mode 100644 index e5c6acb6db..0000000000 --- a/scripts/unit/binary/bump-spec.js +++ /dev/null @@ -1,48 +0,0 @@ -const la = require('lazy-ass') -const snapshot = require('snap-shot-it') -const _ = require('lodash') - -const bump = require('../../binary/bump') - -/* eslint-env mocha */ -describe('bump', () => { - context('remapProjects', () => { - it('returns flat list of projects', () => { - la(bump._PROVIDERS, 'has _PROVIDERS', bump) - const list = bump.remapProjects(bump._PROVIDERS) - - snapshot('list of all projects', list) - }) - }) - - context('getFilterByProvider', () => { - it('returns a filter function without provider name', () => { - const projects = bump.remapProjects(bump._PROVIDERS) - const filter = bump.getFilterByProvider() - // should return ALL projects - const filtered = projects.filter(filter) - - la( - _.isEqual(filtered, projects), - 'should have kept all projects', - filtered, - ) - }) - - it('returns a filter function for circle and linux', () => { - const projects = bump.remapProjects(bump._PROVIDERS) - - la( - projects.length, - 'there should be at least a few projects in the list of projects', - projects, - ) - - const filter = bump.getFilterByProvider('circle', 'linux') - const filtered = projects.filter(filter) - - la(filtered.length, 'there should be at least a few projects', filtered) - snapshot('should have just circle and linux projects', filtered) - }) - }) -}) diff --git a/system-tests/lib/docker.ts b/system-tests/lib/docker.ts index 9d82af21d8..def2684e0a 100644 --- a/system-tests/lib/docker.ts +++ b/system-tests/lib/docker.ts @@ -57,7 +57,12 @@ class DockerProcess extends EventEmitter implements SpawnerResult { for (const k in opts.env) { // skip problematic env vars that we don't wanna preserve from `process.env` - if (['DISPLAY', 'USER', 'HOME', 'USERNAME', 'PATH'].includes(k)) continue + if ( + ['DISPLAY', 'USER', 'HOME', 'USERNAME', 'PATH'].includes(k) + || k.startsWith('npm_') + ) { + continue + } containerCreateEnv.push([k, opts.env[k]].join('=')) } @@ -78,6 +83,7 @@ class DockerProcess extends EventEmitter implements SpawnerResult { Entrypoint: 'bash', Tty: false, // so we can use stdout and stderr Env: containerCreateEnv, + Privileged: true, Binds: [ [path.join(__dirname, '..', '..'), '/cypress'], // map tmpDir to the same absolute path on the container to make it easier to reason about paths in tests diff --git a/system-tests/projects/module-api/.gitignore b/system-tests/projects/module-api/.gitignore new file mode 100644 index 0000000000..c187376076 --- /dev/null +++ b/system-tests/projects/module-api/.gitignore @@ -0,0 +1,12 @@ +.DS_Store +node_modules/ +cypress/videos/ +cypress/plugins/ +test/successful/cypress/videos/ +test/invalid/cypress/videos +test/failing/cypress/videos +test/failing/cypress/screenshots +test/invalid/cypress/screenshots + +test/env/cypress/videos +test/env/cypress/screenshots diff --git a/system-tests/projects/module-api/README.md b/system-tests/projects/module-api/README.md new file mode 100644 index 0000000000..427b5edb82 --- /dev/null +++ b/system-tests/projects/module-api/README.md @@ -0,0 +1,5 @@ +Example and test running specs using [Cypress](https://www.cypress.io) via its [Module API](https://on.cypress.io/module-api) + +## Debugging + +Run tests with `DEBUG=test` environment variable to see more log messages diff --git a/system-tests/projects/module-api/async.js b/system-tests/projects/module-api/async.js new file mode 100644 index 0000000000..86d4a36173 --- /dev/null +++ b/system-tests/projects/module-api/async.js @@ -0,0 +1,15 @@ +const cypress = require('cypress') + +console.log('cypress is', cypress) + +async function testSpec (spec) { + console.assert(spec, 'missing spec filename') + console.log('running test %s', spec) + const result = await cypress.run({ spec }) + + console.log(result) +} +(async () => { + await testSpec('./cypress/integration/a-spec.js') + await testSpec('./cypress/integration/b-spec.js') +})() diff --git a/system-tests/projects/module-api/cypress.json b/system-tests/projects/module-api/cypress.json new file mode 100644 index 0000000000..ff22577dd8 --- /dev/null +++ b/system-tests/projects/module-api/cypress.json @@ -0,0 +1,4 @@ +{ + "pluginsFile": false, + "supportFile": false +} diff --git a/system-tests/projects/module-api/cypress/integration/a-spec.js b/system-tests/projects/module-api/cypress/integration/a-spec.js new file mode 100644 index 0000000000..00bdf0ac63 --- /dev/null +++ b/system-tests/projects/module-api/cypress/integration/a-spec.js @@ -0,0 +1,5 @@ +describe('cypress example - a-spec', () => { + it('works', () => { + expect(true).to.be.true + }) +}) diff --git a/system-tests/projects/module-api/cypress/integration/b-spec.js b/system-tests/projects/module-api/cypress/integration/b-spec.js new file mode 100644 index 0000000000..dc9c71a37b --- /dev/null +++ b/system-tests/projects/module-api/cypress/integration/b-spec.js @@ -0,0 +1,5 @@ +describe('cypress example - b-spec', () => { + it('works', () => { + expect(true).to.be.true + }) +}) diff --git a/system-tests/projects/module-api/index.js b/system-tests/projects/module-api/index.js new file mode 100644 index 0000000000..ad1833fd92 --- /dev/null +++ b/system-tests/projects/module-api/index.js @@ -0,0 +1,20 @@ +const banner = require('terminal-banner').terminalBanner +const allPaths = require('@bahmutov/all-paths') +const cypress = require('cypress') + +console.log('cypress is', cypress) + +const onSuccess = (runResult) => { + banner('Cypresss results') + console.log('%o', runResult) + banner('Results paths') + // TODO find a better way to show all available properties in the runResult object + // maybe a tree representation in the terminal? + console.log(allPaths(runResult).join('\n')) +} + +cypress.run({ + spec: './cypress/integration/a-spec.js', +}) +.then(onSuccess) +.catch(console.error) diff --git a/system-tests/projects/module-api/package.json b/system-tests/projects/module-api/package.json new file mode 100644 index 0000000000..9aaa9a6810 --- /dev/null +++ b/system-tests/projects/module-api/package.json @@ -0,0 +1,22 @@ +{ + "name": "@test-project/cypress-example-module-api", + "version": "0.0.0-development", + "description": "Example running specs using Cypress via its module API", + "private": true, + "main": "index.js", + "scripts": { + "test": "mocha --timeout 30000 test/spec.js" + }, + "devDependencies": { + "@bahmutov/all-paths": "1.0.2", + "chdir-promise": "0.6.2", + "check-more-types": "2.24.0", + "debug": "4.3.3", + "lazy-ass": "1.6.0", + "mocha": "9.2.0", + "mocha-banner": "1.1.2", + "ramda": "0.28.0", + "snap-shot-it": "5.0.1", + "terminal-banner": "1.1.0" + } +} diff --git a/system-tests/projects/module-api/test/env/cypress.json b/system-tests/projects/module-api/test/env/cypress.json new file mode 100644 index 0000000000..ff22577dd8 --- /dev/null +++ b/system-tests/projects/module-api/test/env/cypress.json @@ -0,0 +1,4 @@ +{ + "pluginsFile": false, + "supportFile": false +} diff --git a/system-tests/projects/module-api/test/env/cypress/integration/env-spec.js b/system-tests/projects/module-api/test/env/cypress/integration/env-spec.js new file mode 100644 index 0000000000..005a48daa8 --- /dev/null +++ b/system-tests/projects/module-api/test/env/cypress/integration/env-spec.js @@ -0,0 +1,11 @@ +/// +it('has expected env variables', () => { + // this test checks environment variables + // passed via "cypress.run" arguments + cy.wrap(Cypress.env()).should('deep.include', { + foo: { + bar: 'baz', + }, + another: 42, + }) +}) diff --git a/system-tests/projects/module-api/test/failing/__snapshots__/spec.js b/system-tests/projects/module-api/test/failing/__snapshots__/spec.js new file mode 100644 index 0000000000..2747314647 --- /dev/null +++ b/system-tests/projects/module-api/test/failing/__snapshots__/spec.js @@ -0,0 +1,15 @@ +exports['failing test returns correct number of failing tests 1'] = { + "cypressVersion": "0.0.0", + "totalDuration": "X seconds", + "totalSuites": 2, + "totalTests": 2, + "totalFailed": 1, + "totalPassed": 1, + "totalPending": 0, + "totalSkipped": 0, + "browserName": "electron", + "browserVersion": "1.2.3", + "osName": "darwin", + "osVersion": "16.7.0" +} + diff --git a/system-tests/projects/module-api/test/failing/cypress.json b/system-tests/projects/module-api/test/failing/cypress.json new file mode 100644 index 0000000000..ff22577dd8 --- /dev/null +++ b/system-tests/projects/module-api/test/failing/cypress.json @@ -0,0 +1,4 @@ +{ + "pluginsFile": false, + "supportFile": false +} diff --git a/system-tests/projects/module-api/test/failing/cypress/integration/a-spec.js b/system-tests/projects/module-api/test/failing/cypress/integration/a-spec.js new file mode 100644 index 0000000000..dc0da7daff --- /dev/null +++ b/system-tests/projects/module-api/test/failing/cypress/integration/a-spec.js @@ -0,0 +1,5 @@ +describe('cypress example - a-spec', () => { + it('this fails on purpose', () => { + expect(false).to.be.true + }) +}) diff --git a/system-tests/projects/module-api/test/failing/cypress/integration/b-spec.js b/system-tests/projects/module-api/test/failing/cypress/integration/b-spec.js new file mode 100644 index 0000000000..dc9c71a37b --- /dev/null +++ b/system-tests/projects/module-api/test/failing/cypress/integration/b-spec.js @@ -0,0 +1,5 @@ +describe('cypress example - b-spec', () => { + it('works', () => { + expect(true).to.be.true + }) +}) diff --git a/system-tests/projects/module-api/test/invalid/__snapshots__/spec.js b/system-tests/projects/module-api/test/invalid/__snapshots__/spec.js new file mode 100644 index 0000000000..dac5b0a568 --- /dev/null +++ b/system-tests/projects/module-api/test/invalid/__snapshots__/spec.js @@ -0,0 +1,15 @@ +exports['invalid malformed spec file returns with error code 1'] = { + "cypressVersion": "0.0.0", + "totalDuration": "X seconds", + "totalSuites": 0, + "totalTests": 1, + "totalFailed": 1, + "totalPassed": 0, + "totalPending": 0, + "totalSkipped": 0, + "browserName": "electron", + "browserVersion": "1.2.3", + "osName": "darwin", + "osVersion": "16.7.0" +} + diff --git a/system-tests/projects/module-api/test/invalid/cypress.json b/system-tests/projects/module-api/test/invalid/cypress.json new file mode 100644 index 0000000000..ff22577dd8 --- /dev/null +++ b/system-tests/projects/module-api/test/invalid/cypress.json @@ -0,0 +1,4 @@ +{ + "pluginsFile": false, + "supportFile": false +} diff --git a/system-tests/projects/module-api/test/invalid/cypress/integration/a-spec.js b/system-tests/projects/module-api/test/invalid/cypress/integration/a-spec.js new file mode 100644 index 0000000000..e6938a7f43 --- /dev/null +++ b/system-tests/projects/module-api/test/invalid/cypress/integration/a-spec.js @@ -0,0 +1,4 @@ +// this spec file is invalid on purpose +// to see how Cypress detects "bad" spec file +// eslint-disable-next-line no-undef +foobar() diff --git a/system-tests/projects/module-api/test/invalid/cypress/integration/b-spec.js b/system-tests/projects/module-api/test/invalid/cypress/integration/b-spec.js new file mode 100644 index 0000000000..dc9c71a37b --- /dev/null +++ b/system-tests/projects/module-api/test/invalid/cypress/integration/b-spec.js @@ -0,0 +1,5 @@ +describe('cypress example - b-spec', () => { + it('works', () => { + expect(true).to.be.true + }) +}) diff --git a/system-tests/projects/module-api/test/spec.js b/system-tests/projects/module-api/test/spec.js new file mode 100644 index 0000000000..0494e7917c --- /dev/null +++ b/system-tests/projects/module-api/test/spec.js @@ -0,0 +1,158 @@ +require('mocha-banner').register() + +const cypress = require('cypress') +const chdir = require('chdir-promise') +const join = require('path').join +const fromFolder = join.bind(null, __dirname) +const snapshot = require('snap-shot-it') +const la = require('lazy-ass') +const is = require('check-more-types') +const debug = require('debug')('test') +const R = require('ramda') + +const importantProperties = [ + 'cypressVersion', + 'totalDuration', + 'totalSuites', + 'totalTests', + 'totalFailed', + 'totalPassed', + 'totalPending', + 'totalSkipped', + 'browserName', + 'browserVersion', + 'osName', + 'osVersion', +] + +const pickImportant = R.pick(importantProperties) + +const normalize = (output) => { + la(is.unemptyString(output.cypressVersion), 'has Cypress version', output) + la(is.positive(output.totalDuration), 'has duration', output) + la(is.unemptyString(output.browserVersion), 'has browserVersion', output) + la(is.unemptyString(output.osName), 'has osName', output) + la(is.unemptyString(output.osVersion), 'has osVersion', output) + + output.cypressVersion = '0.0.0' + output.totalDuration = 'X seconds' + output.browserVersion = '1.2.3' + output.osName = 'darwin' + output.osVersion = '16.7.0' + + return output +} + +describe('successful tests', () => { + const projectFolder = fromFolder('successful') + + beforeEach(() => { + chdir.to(projectFolder) + }) + + afterEach(chdir.back) + + it('returns with all successful tests', () => { + return cypress.run() + .then(R.tap(debug)) + .then(normalize) + .then(pickImportant) + .then(snapshot) + }) + + it('runs specific spec', () => { + return cypress.run({ + spec: 'cypress/integration/a-spec.js', + }).then(R.tap(debug)) + .then((result) => { + la(result.totalTests === 1, 'there should be single test', result.totalTests) + }) + }) + + it('runs specific spec using absolute path', () => { + const absoluteSpec = join(projectFolder, 'cypress/integration/a-spec.js') + + debug('absolute path to the spec: %s', absoluteSpec) + + return cypress.run({ + spec: absoluteSpec, + }).then(R.tap(debug)) + .then((result) => { + la(result.totalTests === 1, 'there should be single test', result.totalTests) + }) + }) + + it('runs a single spec using wildcard', () => { + return cypress.run({ + spec: 'cypress/integration/a-*.js', + }).then(R.tap(debug)) + .then((result) => { + la(result.totalTests === 1, 'there should be single test', result.totalTests) + }) + }) + + it('runs both found specs using wildcard', () => { + return cypress.run({ + spec: 'cypress/integration/a-*.js,cypress/integration/b-*.js', + }).then(R.tap(debug)) + .then((result) => { + la(result.totalTests === 2, 'found both tests', result.totalTests) + }) + }) +}) + +describe('env variables', () => { + const projectFolder = fromFolder('env') + + beforeEach(() => { + chdir.to(projectFolder) + }) + + afterEach(chdir.back) + + it('passes environment variables in the object', () => { + return cypress.run({ + spec: 'cypress/integration/env-spec.js', + env: { + foo: { + bar: 'baz', + }, + another: 42, + }, + }) + }) +}) + +describe('failing test', () => { + beforeEach(() => { + chdir.to(fromFolder('failing')) + }) + + afterEach(chdir.back) + + it('returns correct number of failing tests', () => { + return cypress.run() + .then(normalize) + .then(pickImportant) + .then(snapshot) + }) +}) + +// https://github.com/cypress-io/cypress-test-module-api/issues/3 +describe('invalid malformed spec file', () => { + beforeEach(() => { + chdir.to(fromFolder('invalid')) + }) + + afterEach(chdir.back) + + it('returns with error code', () => { + // test has reference error on load + return cypress.run({ + spec: './cypress/integration/a-spec.js', + }) + .then(normalize) + .then(pickImportant) + .then(snapshot) + }) +}) diff --git a/system-tests/projects/module-api/test/successful/__snapshots__/spec.js b/system-tests/projects/module-api/test/successful/__snapshots__/spec.js new file mode 100644 index 0000000000..32bd0f6a1f --- /dev/null +++ b/system-tests/projects/module-api/test/successful/__snapshots__/spec.js @@ -0,0 +1,15 @@ +exports['successful tests returns with all successful tests 1'] = { + "cypressVersion": "0.0.0", + "totalDuration": "X seconds", + "totalSuites": 2, + "totalTests": 2, + "totalFailed": 0, + "totalPassed": 2, + "totalPending": 0, + "totalSkipped": 0, + "browserName": "electron", + "browserVersion": "1.2.3", + "osName": "darwin", + "osVersion": "16.7.0" +} + diff --git a/system-tests/projects/module-api/test/successful/cypress.json b/system-tests/projects/module-api/test/successful/cypress.json new file mode 100644 index 0000000000..ff22577dd8 --- /dev/null +++ b/system-tests/projects/module-api/test/successful/cypress.json @@ -0,0 +1,4 @@ +{ + "pluginsFile": false, + "supportFile": false +} diff --git a/system-tests/projects/module-api/test/successful/cypress/integration/a-spec.js b/system-tests/projects/module-api/test/successful/cypress/integration/a-spec.js new file mode 100644 index 0000000000..00bdf0ac63 --- /dev/null +++ b/system-tests/projects/module-api/test/successful/cypress/integration/a-spec.js @@ -0,0 +1,5 @@ +describe('cypress example - a-spec', () => { + it('works', () => { + expect(true).to.be.true + }) +}) diff --git a/system-tests/projects/module-api/test/successful/cypress/integration/b-spec.js b/system-tests/projects/module-api/test/successful/cypress/integration/b-spec.js new file mode 100644 index 0000000000..dc9c71a37b --- /dev/null +++ b/system-tests/projects/module-api/test/successful/cypress/integration/b-spec.js @@ -0,0 +1,5 @@ +describe('cypress example - b-spec', () => { + it('works', () => { + expect(true).to.be.true + }) +}) diff --git a/system-tests/projects/module-api/yarn.lock b/system-tests/projects/module-api/yarn.lock new file mode 100644 index 0000000000..56c3645c1d --- /dev/null +++ b/system-tests/projects/module-api/yarn.lock @@ -0,0 +1,815 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@bahmutov/all-paths@1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@bahmutov/all-paths/-/all-paths-1.0.2.tgz#9ae0dcdf9022dd6e5e14d7fda3479e6a330d035b" + integrity "sha1-muDc35Ai3W5eFNf9o0eeajMNA1s= sha512-kqeMYh7e9yXWSm7LdQp4BZ4Igxk25lM2Jtw4+G83ro5nFvbHAp3ZmY2na/AVk57+CpZDH/sCXxyKFwkdisnkbw==" + dependencies: + lodash.isplainobject "4.0.6" + lodash.range "3.2.0" + +"@bahmutov/data-driven@1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@bahmutov/data-driven/-/data-driven-1.0.0.tgz#6897061cd61a0afc26a7079527bfbb1073b4228a" + integrity "sha1-aJcGHNYaCvwmpweVJ7+7EHO0Ioo= sha512-YqW3hPS0RXriqjcCrLOTJj+LWe3c8JpwlL83k1ka1Q8U05ZjAKbGQZYeTzUd0NFEnnfPtsUiKGpFEBJG6kFuvg==" + dependencies: + check-more-types "2.24.0" + lazy-ass "1.6.0" + +"@ungap/promise-all-settled@1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" + integrity "sha1-qlgEJxHW4ydd033Fl+XTHowpCkQ= sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==" + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity "sha1-y7muJWv3UK8eqzRPIpqif+lLo0g= sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity "sha1-w7M6te42DYbg5ijwRorn7yfWVN8= sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= sha512-wFUFA5bg5dviipbQQ32yOQhl6gcJaJXiHE7dvR8VYPG97+J/GNC5FKGepKdEDUFeXRzDxPF1X/Btc8L+v7oqIQ==" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity "sha1-CCyyyJyf6GWaMRpTvWpNxTAdswQ= sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + +ansi-styles@^2.0.1, ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity "sha1-7dgDYornHATIWuegkG7a00tkiTc= sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==" + dependencies: + color-convert "^2.0.1" + +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity "sha1-wFV8CWrzLxBhmPT04qODU343hxY= sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==" + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity "sha1-JG9Q88p4oyQPbJl+ipvR6sSeSzg= sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity "sha1-6D46fj8wCzTLnYf2FfoMvzV2kO4= sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity "sha1-dfUC7q+f/eQvyYgpZFvk6na9ni0= sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + +bluebird@^3.5.1: + version "3.7.2" + resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity "sha1-nyKcFb4nJFT/qXOs4NvueaGww28= sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0= sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity "sha1-NFThpGLujVmeI23zNs2epPiv4Qc= sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==" + dependencies: + fill-range "^7.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity "sha1-uqVZ7hTO1zRSIputcyZGfGH6vWA= sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity "sha1-VoW5XrIJrJwMF3Rnd4ychN9Yupo= sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + +chalk@^1.1.1: + version "1.1.3" + resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity "sha1-qsTit3NKdAhnrrFr8CqtVWoeegE= sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==" + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chdir-promise@0.6.2: + version "0.6.2" + resolved "https://registry.npmjs.org/chdir-promise/-/chdir-promise-0.6.2.tgz#d4cfa0a96a112a8149341b69e2866d162f0e2dbd" + integrity "sha1-1M+gqWoRKoFJNBtp4oZtFi8OLb0= sha512-EG5MutQt4qTxoQPfBtPCfU1A/MqborgaO66xrPSD/dRTB40OLN0wy+YAo5ZAw7DawhtCPdZHAdQ206fyWkhoiw==" + dependencies: + bluebird "^3.5.1" + check-more-types "2.24.0" + debug "3.1.0" + lazy-ass "1.6.0" + +check-more-types@2.24.0: + version "2.24.0" + resolved "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==" + +chokidar@3.5.3: + version "3.5.3" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity "sha1-HPN8hwe5Mr0a8a4iwEMuKs0ZA70= sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==" + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +ci-info@^1.0.0: + version "1.6.0" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" + integrity "sha1-LKINu5zrMtRSSmgzAzE/AwSx5Jc= sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity "sha1-oCZe5lVHb8gHrqnfPfjfd4OAi08= sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==" + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM= sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==" + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI= sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + +common-tags@1.8.0: + version "1.8.0" + resolved "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" + integrity "sha1-jjFT5ULUo56bEFVENK+q+YlWqTc= sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + +debug@3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE= sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==" + dependencies: + ms "2.0.0" + +debug@4.3.3: + version "4.3.3" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity "sha1-BCZuC3CpjURi5uKI44JZITMytmQ= sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==" + dependencies: + ms "2.1.2" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity "sha1-qkcte/Zg6xXzSU79UxyrfypwmDc= sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==" + +diff@5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity "sha1-ftatdthZ0DB4fsNYVfWx2vMdhSs= sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==" + +diff@^1.3.2: + version "1.4.0" + resolved "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" + integrity "sha1-fyjS657nsVqX79ic5j3P2qPMur8= sha512-VzVc42hMZbYU9Sx/ltb7KYuQ6pqAw+cbFWVy4XKdkuEL2CFaRLGEnISPs7YdzaUGpi+CpIqvRmu7hPQ4T7EQ5w==" + +disparity@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/disparity/-/disparity-2.0.0.tgz#57ddacb47324ae5f58d2cc0da886db4ce9eeb718" + integrity "sha1-V92stHMkrl9Y0swNqIbbTOnutxg= sha512-caMA9oIQbDeobWc9MVUF0+o1APVo0UZnfD482x0RaLLAzpAc6Dla2mk5ef7onsG91tziIRr41rj4sVt3BOLLZw==" + dependencies: + ansi-styles "^2.0.1" + diff "^1.3.2" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity "sha1-6Bj9ac5cz8tARZT4QpY79TFkzDc= sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity "sha1-2M/ccACWXFoBdLSoLqpcBVJ0LkA= sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + +escape-quotes@1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/escape-quotes/-/escape-quotes-1.0.2.tgz#b4896d4a6cf82dd5b33f49b713408c6380f6cd97" + integrity "sha1-tIltSmz4LdWzP0m3E0CMY4D2zZc= sha512-JpLFzklNReeakCpyj59s78P5F72q0ZUpDnp2BuIk9TtTjj2HMsgiWBChw17BlZT8dRhMtmSb1jE2+pTP1iFYyw==" + dependencies: + escape-string-regexp "^1.0.5" + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity "sha1-FLqDpdNz49MR5a/KKc9b+tllvzQ= sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity "sha1-GRmmp8df44ssfHflGYU12prN2kA= sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==" + dependencies: + to-regex-range "^5.0.1" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity "sha1-TJKBnstwg1YeT0okCoa+UZj1Nvw= sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==" + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity "sha1-jKb+MyBp/6nTJMMnGYxZglnOskE= sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==" + +folktale@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/folktale/-/folktale-2.0.1.tgz#6dc26a65565aefdef9520223e022dddf5b8d8462" + integrity "sha1-bcJqZVZa7975UgIj4CLd31uNhGI= sha512-3kDSWVkSlErHIt/dC73vu+5zRqbW1mlnL46s2QfYN7Ps0JcS9MVtuLCrDQOBa7sanA+d9Fd8F+bn0VcyNe68Jw==" + +folktale@2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/folktale/-/folktale-2.1.0.tgz#3332b9e37b4b92150788b72bd0e2ddd8d70e4096" + integrity "sha1-MzK543tLkhUHiLcr0OLd2NcOQJY= sha512-eG9lWx/MAOPoFllqASH7d1+QMl6qCd+E+rOPSWgulFPKG55506kXKQZLnMg3fMHFZUWY/2POZUPc/MH7048nIg==" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity "sha1-FQStJSMVjKpA20onh8sBQRmU6k8= sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro= sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==" + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity "sha1-T5RBKoLbMvNuOwuXQfipf+sDH34= sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity "sha1-hpgyxYA0/mikCTwX3BXoNA2EAcQ= sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==" + dependencies: + is-glob "^4.0.1" + +glob@7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity "sha1-0VU1r3cy4C6Uj0xBYovZECk/YCM= sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity "sha1-8nNdwig2dPpnR4sQGBBZNVw2nl4= sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s= sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + +has-only@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/has-only/-/has-only-1.1.1.tgz#0ed2b101e73a2226254421464c65e381affcce66" + integrity "sha1-DtKxAec6IiYlRCFGTGXjga/8zmY= sha512-3GuFy9rDw0xvovCHb4SOKiRImbZ+a8boFBUyGNRPVd2mRyQOzYdau5G9nodUXC1ZKYN59hrHFkW1lgBQscYfTg==" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity "sha1-hK5l+n6vsWX922FWauFLrwVmTw8= sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w= sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity "sha1-6h9/O4DwZCNug0cPhsCcJU+0Wwk= sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==" + dependencies: + binary-extensions "^2.0.0" + +is-ci@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz#247e4162e7860cebbdaf30b774d6b0ac7dcfe7a5" + integrity "sha1-JH5BYueGDOu9rzC3dNawrH3P56U= sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==" + dependencies: + ci-info "^1.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity "sha1-8Rb4Bk/pCz94RKOJl8C3UFEmnx0= sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity "sha1-ZPYeQsu7LuwgcanawLKLoeZdUIQ= sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==" + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss= sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity "sha1-ReQuN/zPH0Dajl927iFRWEDAkoc= sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity "sha1-PybHaoCVk7Ur+i7LVxDtJ3m1Iqc= sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + +its-name@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/its-name/-/its-name-1.0.0.tgz#2065f1883ecb568c65f7112ddbf123401fae4af0" + integrity "sha1-IGXxiD7LVoxl9xEt2/EjQB+uSvA= sha512-GYUWFxViqxDvGzsNEItTEuOqqAQVx29Xl9Lh5YUqyJd6gPHTCMiIbjqcjjyUzsBUqEcgwIdRoydwgfFw1oYbhg==" + +js-yaml@4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity "sha1-wftl+PUBeQHN0slRhkuhhFihBgI= sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==" + dependencies: + argparse "^2.0.1" + +jsesc@2.5.1: + version "2.5.1" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe" + integrity "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4= sha512-Bl0oKW/TMSVifAQLbupbpOrssQ3CC+lyhZZjofI19OuKtwq8/A9KtxDA22wppbeiFqAQdTnVkDR8ymtRFAxtYw==" + +lazy-ass@1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity "sha1-eZllXoZGwX8In90YfRUNMyTVRRM= sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity "sha1-VTIeswn+u8WcSAHZMackUqaB0oY= sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==" + dependencies: + p-locate "^5.0.0" + +lodash.isplainobject@4.0.6: + version "4.0.6" + resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + +lodash.range@3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/lodash.range/-/lodash.range-3.2.0.tgz#f461e588f66683f7eadeade513e38a69a565a15d" + integrity "sha1-9GHliPZmg/fq3q3lE+OKaaVloV0= sha512-Fgkb7SinmuzqgIhNhAElo0BL/R1rHCnhwSZf78omqSwvWqD0kD2ssOAutQonDKH/ldS8BxA72ORYI09qAY9CYg==" + +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity "sha1-P727lbRoOsn8eFER55LlWNSr1QM= sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==" + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +minimatch@3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM= sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==" + dependencies: + brace-expansion "^1.1.7" + +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity "sha1-Gc0ZS/0+Qo8EmnCBfAONiatL41s= sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==" + +mkdirp@0.5.1: + version "0.5.1" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==" + dependencies: + minimist "0.0.8" + +mocha-banner@1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/mocha-banner/-/mocha-banner-1.1.2.tgz#7cac0e35daf805dfe2adb6d8158cab1085bdf6a4" + integrity "sha1-fKwONdr4Bd/irbbYFYyrEIW99qQ= sha512-dsAEp32v2vmPM7CP7cmdDLjcWIohoSPV2bTOltyE0Bb8A1raVCSsLMBJlWpr/+lDoVlrPwwsM1SNCYqeyznFeQ==" + dependencies: + its-name "1.0.0" + terminal-banner "1.1.0" + +mocha@9.2.0: + version "9.2.0" + resolved "https://registry.npmjs.org/mocha/-/mocha-9.2.0.tgz#2bfba73d46e392901f877ab9a47b7c9c5d0275cc" + integrity "sha1-K/unPUbjkpAfh3q5pHt8nF0Cdcw= sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q==" + dependencies: + "@ungap/promise-all-settled" "1.1.2" + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.3" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + growl "1.10.5" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "3.0.4" + ms "2.1.3" + nanoid "3.2.0" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + which "2.0.2" + workerpool "6.2.0" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk= sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity "sha1-V0yBOM4dK1hh8LRFedut1gxmFbI= sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + +nanoid@3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" + integrity "sha1-YmZ1Itpmc5ccypFqbT7/P0Ff+Aw= sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU= sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity "sha1-WDsap3WWHUsROsF9nFC6753Xa9E= sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==" + dependencies: + wrappy "1" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity "sha1-4drMvnjQ0TiMoYxk/qOOPlfjcGs= sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==" + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity "sha1-g8gxXGeFAF470CGDlBHJ4RDm2DQ= sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==" + dependencies: + p-limit "^3.0.2" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity "sha1-UTvb4tO5XXdi6METfvoZXGxhtbM= sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity "sha1-F0uSaHNVNP+8es5r9TpanhtcX18= sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity "sha1-O6ODNzNkbZ0+SZWUbBNlpn+wekI= sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + +ramda@0.25.0: + version "0.25.0" + resolved "https://registry.npmjs.org/ramda/-/ramda-0.25.0.tgz#8fdf68231cffa90bc2f9460390a0cb74a29b29a9" + integrity "sha1-j99oIxz/qQvC+UYDkKDLdKKbKak= sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ==" + +ramda@0.28.0: + version "0.28.0" + resolved "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz#acd785690100337e8b063cab3470019be427cc97" + integrity "sha1-rNeFaQEAM36LBjyrNHABm+QnzJc= sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==" + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity "sha1-32+ENy8CcNxlzfYpE0mrekc9Tyo= sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==" + dependencies: + safe-buffer "^5.1.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity "sha1-dKNwvYVxFuJFspzJc0DNQxoCpsc= sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==" + dependencies: + picomatch "^2.2.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity "sha1-jGStX9MNqxyXbiNE/+f3kqam30I= sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity "sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY= sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity "sha1-765diPRdeSQUHai1w6en5mP+/rg= sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==" + dependencies: + randombytes "^2.1.0" + +snap-shot-compare@2.7.1: + version "2.7.1" + resolved "https://registry.npmjs.org/snap-shot-compare/-/snap-shot-compare-2.7.1.tgz#e7ac19d2a0385414fa8f847c4d01855f49383fdb" + integrity "sha1-56wZ0qA4VBT6j4R8TQGFX0k4P9s= sha512-qJPyYskLyuOOON1rFjO5xHmeVduaNJGd6AgeWqq8F6rVE+n79Jr7wdML2qrODoAeb63zztd5JB9Cc8WSWV/rig==" + dependencies: + check-more-types "2.24.0" + disparity "2.0.0" + folktale "2.0.1" + lazy-ass "1.6.0" + strip-ansi "4.0.0" + variable-diff "1.1.0" + +snap-shot-core@6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/snap-shot-core/-/snap-shot-core-6.0.1.tgz#b74dc57234848dd96eaa68e40d3bdb5a9c350e61" + integrity "sha1-t03FcjSEjdluqmjkDTvbWpw1DmE= sha512-3jbkQIrpd5PQXOSRTsa7fAKw9vNI4uIdo7bnznOpIqKBLSzWTuakRC6b334c2rqPAI/QrXokcs1vEGJTeM1dsQ==" + dependencies: + check-more-types "2.24.0" + common-tags "1.8.0" + debug "3.1.0" + escape-quotes "1.0.2" + folktale "2.1.0" + is-ci "1.1.0" + jsesc "2.5.1" + lazy-ass "1.6.0" + mkdirp "0.5.1" + ramda "0.25.0" + +snap-shot-it@5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/snap-shot-it/-/snap-shot-it-5.0.1.tgz#0755fc146c1264bfafe7b9e77e8524a2bdd56178" + integrity "sha1-B1X8FGwSZL+v57nnfoUkor3VYXg= sha512-TSlYi4ceHhd/Fcy3EE6Db+kjPPI81eAXOhWm69BfPQ5MriPV083CDa3grFW/yJE/bXEVMZ7MB/v84tQkqWjBJQ==" + dependencies: + "@bahmutov/data-driven" "1.0.0" + check-more-types "2.24.0" + debug "3.1.0" + has-only "1.1.1" + ramda "0.25.0" + snap-shot-compare "2.7.1" + snap-shot-core "6.0.1" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity "sha1-JpxxF9J7Ba0uU2gwqOyJXvnG0BA= sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==" + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity "sha1-qEeQIusaw2iocTibY1JixQXuNo8= sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==" + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity "sha1-nibGPTD1NEPpSJSVshBdN7Z6hdk= sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==" + dependencies: + ansi-regex "^5.0.1" + +strip-json-comments@3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity "sha1-MfEoGzgyYwQ0gxwxDAHMzajL4AY= sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + +supports-color@8.1.1: + version "8.1.1" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity "sha1-zW/BfihQDP9WwbhsCn/UpUpzAFw= sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==" + dependencies: + has-flag "^4.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo= sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==" + dependencies: + has-flag "^4.0.0" + +terminal-banner@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/terminal-banner/-/terminal-banner-1.1.0.tgz#ef81ce7d9d7e541a81d09eb2c0257c3d5463c3ea" + integrity "sha1-74HOfZ1+VBqB0J6ywCV8PVRjw+o= sha512-A70B8Io5gGOTKQuoqU6LUPLouNd9DvFLgw3cPh6bfrQjdy7HWW1t04VJfQwjTnygTVDX0xremaj1cg3SQaCGyg==" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ= sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==" + dependencies: + is-number "^7.0.0" + +variable-diff@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/variable-diff/-/variable-diff-1.1.0.tgz#d2bd5c66db76c13879d96e6a306edc989df978da" + integrity "sha1-0r1cZtt2wTh52W5qMG7cmJ35eNo= sha512-0Jk/MsCNtL/fCuVIbsLxwXpABGZCzN57btcPbSsjOOAwkdHJ3Y58fo8BoUfG7jghnvglbwo+5Hk1KOJ2W2Ormw==" + dependencies: + chalk "^1.1.1" + object-assign "^4.0.1" + +which@2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity "sha1-fGqN0KY2oDJ+ELWckobu6T8/UbE= sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==" + dependencies: + isexe "^2.0.0" + +workerpool@6.2.0: + version "6.2.0" + resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" + integrity "sha1-gn2Tyboj7iAZw/+v9cJ/zOoonos= sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity "sha1-Z+FFz/UQpqaYS98RUpEdadLrnkM= sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==" + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity "sha1-f0k00PfKjFb5UxSTndzS3ZHOHVU= sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity "sha1-tCiQ8UVmeW+Fro46JSkNIF8VSlQ= sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==" + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity "sha1-LrfcOwKJcY/ClfNidThFxBoMlO4= sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity "sha1-8TH5ImkRrl2a04xDL+gJNmwjJes= sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==" + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@16.2.0: + version "16.2.0" + resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity "sha1-HIK/D2tqZur85+8w43b0mhJHf2Y= sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==" + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity "sha1-ApTrPe4FAo0x7hpfosVWpqrxChs= sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" diff --git a/system-tests/scripts/bootstrap-docker-container.sh b/system-tests/scripts/bootstrap-docker-container.sh index 058f215c54..cca161f947 100755 --- a/system-tests/scripts/bootstrap-docker-container.sh +++ b/system-tests/scripts/bootstrap-docker-container.sh @@ -33,8 +33,10 @@ cd $TEST_PROJECT_DIR export CYPRESS_INSTALL_BINARY=$ZIP_PATH export CYPRESS_CACHE_FOLDER=/tmp/CYPRESS_CACHE_FOLDER/ +export npm_config_cache=/tmp/npm_config_cache/ +export npm_config_package_lock=false -npm install --save-dev --unsafe-perm --allow-root $CLI_PATH +npx npm@latest install --unsafe-perm --allow-root --force file:$CLI_PATH PATH=$PATH:./node_modules/.bin diff --git a/system-tests/test-binary/module_api_spec.ts b/system-tests/test-binary/module_api_spec.ts new file mode 100644 index 0000000000..9b2cfc3f40 --- /dev/null +++ b/system-tests/test-binary/module_api_spec.ts @@ -0,0 +1,13 @@ +import systemTests from '../lib/system-tests' + +describe('module API', () => { + systemTests.it('can run module API Mocha spec', { + timeout: 240000, + dockerImage: 'cypress/base:12', + withBinary: true, + project: 'module-api', + browser: 'electron', + command: 'yarn', + args: ['test'], + }) +}) diff --git a/yarn.lock b/yarn.lock index 6a293925f7..eb06de796d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2267,21 +2267,6 @@ through2 "^2.0.0" watchify "3.11.1" -"@cypress/bumpercar@2.0.12": - version "2.0.12" - resolved "https://registry.yarnpkg.com/@cypress/bumpercar/-/bumpercar-2.0.12.tgz#5e0231db1b4782fb92ce96c84ca0c2f1477c3201" - integrity sha512-O+NvKPWbKvNzotX+Orb4+dI/0KTydco4EdNBilG+8muItTZ7bWjOs+iDZY48Bjkuh78+k42eBgAEgbJWYSqSbA== - dependencies: - "@cypress/coffee-script" "0.1.2" - axios "0.19.0" - bluebird "3.7.1" - check-more-types "^2.24.0" - debug "^3.1.0" - lazy-ass "^1.6.0" - lodash "^4.17.4" - parse-github-repo-url "^1.4.1" - ramda "^0.26.0" - "@cypress/code-coverage@3.8.1": version "3.8.1" resolved "https://registry.yarnpkg.com/@cypress/code-coverage/-/code-coverage-3.8.1.tgz#913f34b1063423cc881988fa3b452da170aea5b6" @@ -2324,13 +2309,6 @@ js-yaml "3.14.1" nyc "15.1.0" -"@cypress/coffee-script@0.1.2": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@cypress/coffee-script/-/coffee-script-0.1.2.tgz#2721cf60ef65ce47b3895aa0b8e84771868bc011" - integrity sha1-JyHPYO9lzkeziVqguOhHcYaLwBE= - dependencies: - coffee-script "1.12.5" - "@cypress/commit-info@2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@cypress/commit-info/-/commit-info-2.2.0.tgz#6086d478975edb7ac7c9ffdd5cfd5be2b9fe44f2" @@ -5397,16 +5375,6 @@ universal-user-agent "^2.0.0" url-template "^2.0.8" -"@octokit/rest@18.10.0", "@octokit/rest@^18.0.0": - version "18.10.0" - resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.10.0.tgz#8a0add9611253e0e31d3ed5b4bc941a3795a7648" - integrity sha512-esHR5OKy38bccL/sajHqZudZCvmv4yjovMJzyXlphaUo7xykmtOdILGJ3aAm0mFHmMLmPFmDMJXf39cAjNJsrw== - dependencies: - "@octokit/core" "^3.5.1" - "@octokit/plugin-paginate-rest" "^2.16.0" - "@octokit/plugin-request-log" "^1.0.4" - "@octokit/plugin-rest-endpoint-methods" "^5.9.0" - "@octokit/rest@^16.28.4": version "16.43.2" resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.43.2.tgz#c53426f1e1d1044dee967023e3279c50993dd91b" @@ -5429,6 +5397,16 @@ once "^1.4.0" universal-user-agent "^4.0.0" +"@octokit/rest@^18.0.0": + version "18.10.0" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.10.0.tgz#8a0add9611253e0e31d3ed5b4bc941a3795a7648" + integrity sha512-esHR5OKy38bccL/sajHqZudZCvmv4yjovMJzyXlphaUo7xykmtOdILGJ3aAm0mFHmMLmPFmDMJXf39cAjNJsrw== + dependencies: + "@octokit/core" "^3.5.1" + "@octokit/plugin-paginate-rest" "^2.16.0" + "@octokit/plugin-request-log" "^1.0.4" + "@octokit/plugin-rest-endpoint-methods" "^5.9.0" + "@octokit/types@^2.0.0", "@octokit/types@^2.0.1": version "2.16.2" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-2.16.2.tgz#4c5f8da3c6fecf3da1811aef678fda03edac35d2" @@ -11160,14 +11138,6 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -axios@0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8" - integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ== - dependencies: - follow-redirects "1.5.10" - is-buffer "^2.0.2" - axios@0.21.2: version "0.21.2" resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.2.tgz#21297d5084b2aeeb422f5d38e7be4fbb82239017" @@ -14359,7 +14329,7 @@ coffee-loader@^0.9.0: dependencies: loader-utils "^1.0.2" -coffee-script@1.12.5, coffee-script@^1.10.0: +coffee-script@^1.10.0: version "1.12.5" resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.12.5.tgz#809f4585419112bbfe46a073ad7543af18c27346" integrity sha1-gJ9FhUGRErv+RqBzrXVDrxjCc0Y= @@ -16237,7 +16207,7 @@ debug@2.6.8: dependencies: ms "2.0.0" -debug@3.1.0, debug@=3.1.0, debug@~3.1.0: +debug@3.1.0, debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== @@ -19857,13 +19827,6 @@ folktale@2.3.2: resolved "https://registry.yarnpkg.com/folktale/-/folktale-2.3.2.tgz#38231b039e5ef36989920cbf805bf6b227bf4fd4" integrity sha512-+8GbtQBwEqutP0v3uajDDoN64K2ehmHd0cjlghhxh0WpcfPzAIjPA03e1VvHlxL02FVGR0A6lwXsNQKn3H1RNQ== -follow-redirects@1.5.10: - version "1.5.10" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" - integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== - dependencies: - debug "=3.1.0" - follow-redirects@^1.0.0, follow-redirects@^1.14.0: version "1.14.5" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.5.tgz#f09a5848981d3c772b5392309778523f8d85c381" @@ -22929,7 +22892,7 @@ is-buffer@^1.0.2, is-buffer@^1.1.0, is-buffer@^1.1.3, is-buffer@^1.1.5, is-buffe resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@^2.0.0, is-buffer@^2.0.2, is-buffer@~2.0.3: +is-buffer@^2.0.0, is-buffer@~2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== @@ -26558,15 +26521,6 @@ make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: dependencies: semver "^6.0.0" -make-empty-github-commit@cypress-io/make-empty-github-commit#4a592aedb776ba2f4cc88979055315a53eec42ee: - version "0.0.0-development" - resolved "https://codeload.github.com/cypress-io/make-empty-github-commit/tar.gz/4a592aedb776ba2f4cc88979055315a53eec42ee" - dependencies: - "@octokit/rest" "18.10.0" - debug "3.1.0" - minimist "1.2.5" - parse-github-repo-url "1.4.1" - make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -30149,7 +30103,7 @@ parse-filepath@^1.0.1: map-cache "^0.2.0" path-root "^0.1.1" -parse-github-repo-url@1.4.1, parse-github-repo-url@^1.3.0, parse-github-repo-url@^1.4.1: +parse-github-repo-url@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz#9e7d8bb252a6cb6ba42595060b7bf6df3dbc1f50" integrity sha1-nn2LslKmy2ukJZUGC3v23z28H1A= @@ -32609,7 +32563,7 @@ ramda@0.25.0, ramda@^0.25.0: resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.25.0.tgz#8fdf68231cffa90bc2f9460390a0cb74a29b29a9" integrity sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ== -ramda@0.26.1, ramda@^0.26.0: +ramda@0.26.1: version "0.26.1" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ== @@ -39396,7 +39350,7 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" -url-parse@1.5.6: +url-parse@1.5.6, url-parse@^1.4.3, url-parse@^1.4.7: version "1.5.6" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.6.tgz#b2a41d5a233645f3c31204cc8be60e76a15230a2" integrity sha512-xj3QdUJ1DttD1LeSfvJlU1eiF1RvBSBfUu8GplFGdUzSO28y5yUtEl7wb//PI4Af6qh0o/K8545vUmucRrfWsw== @@ -39404,14 +39358,6 @@ url-parse@1.5.6: querystringify "^2.1.1" requires-port "^1.0.0" -url-parse@^1.4.3, url-parse@^1.4.7: - version "1.5.2" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.2.tgz#a4eff6fd5ff9fe6ab98ac1f79641819d13247cda" - integrity sha512-6bTUPERy1muxxYClbzoRo5qtQuyoGEbzbQvi0SW4/8U8UyVkAQhWFBlnigqJkRm4su4x1zDQfNbEzWkt+vchcg== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - url-template@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21"