diff --git a/circle.yml b/circle.yml index 02bf785edd1b..18d0cbea5cd7 100644 --- a/circle.yml +++ b/circle.yml @@ -77,14 +77,14 @@ commands: - run: name: Install latest Google Chrome (stable) command: | - if [ << parameters.browser >> == "chrome" ]; then + if [ <> == "chrome" ]; then echo "**** Running Chrome tests. Installing latest stable version of Google Chrome. ****" apt-get update apt-get install google-chrome-stable -y echo "**** Location of Google Chrome Installation: "`which google-chrome`" ****" echo "**** Google Chrome Version: "`google-chrome --version`" ****" else - echo "**** Not updating Chrome. Running tests in '<< parameters.browser >>' ****" + echo "**** Not updating Chrome. Running tests in '<>' ****" fi run-driver-integration-tests: @@ -105,7 +105,7 @@ commands: if [[ -v PACKAGES_RECORD_KEY ]]; then # internal PR CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \ - yarn cypress:run --record --parallel --group 5x-driver-<< parameters.browser >> --browser << parameters.browser >> + yarn cypress:run --record --parallel --group 5x-driver-<> --browser <> else # external PR TESTFILES=$(circleci tests glob "cypress/integration/**/*_spec.*" | circleci tests split --total=$CIRCLE_NODE_TOTAL) @@ -114,7 +114,7 @@ commands: if [[ -z "$TESTFILES" ]]; then echo "Empty list of test files" fi - yarn cypress:run --browser << parameters.browser >> --spec $TESTFILES + yarn cypress:run --browser <> --spec $TESTFILES fi working_directory: packages/driver - verify-mocha-results @@ -136,7 +136,7 @@ commands: command: | CYPRESS_KONFIG_ENV=production \ CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \ - yarn workspace @packages/runner cypress:run --record --parallel --group runner-integration-<< parameters.browser >> --browser <> + yarn workspace @packages/runner cypress:run --record --parallel --group runner-integration-<> --browser <> - store_test_results: path: /tmp/cypress - store_artifacts: @@ -156,7 +156,7 @@ commands: - attach_workspace: at: ~/ - run: - command: yarn workspace @packages/server test ./test/e2e/<< parameters.chunk >>*spec* --browser << parameters.browser >> + command: yarn workspace @packages/server test ./test/e2e/<>*spec* --browser <> - verify-mocha-results - store_test_results: path: /tmp/cypress @@ -194,15 +194,30 @@ commands: ## by default, assert that at least 1 test ran default: 0 steps: - - run: yarn verify:mocha:results << parameters.expectedResultCount >> + - run: yarn verify:mocha:results <> + clone-repo-and-checkout-release-branch: + description: | + Clones an external repo and then checks out the branch that matches NEXT_DEV_VERSION otherwise uses 'master' branch. + parameters: + repo: + description: "Name of the github repo to clone like: cypress-example-kitchensink" + type: string + steps: + - attach_workspace: + at: ~/ + - run: + name: "Cloning test project: <>" + command: | + git clone --depth 1 --no-single-branch https://github.com/cypress-io/<>.git /tmp/<> + cd /tmp/<> && (git checkout $NEXT_DEV_VERSION || true) test-binary-against-repo: description: | Takes the built binary and NPM package, clones given example repo and runs the new version of Cypress against it. parameters: repo: - description: Name of the repo like "cypress-example-kitchensink" + description: "Name of the github repo to clone like: cypress-example-kitchensink" type: string browser: description: Name of the browser to use, like "electron", "chrome", "firefox" @@ -232,18 +247,17 @@ commands: # make sure the binary and NPM package files are present - run: ls -l - run: ls -l cypress.zip cypress.tgz - - run: - name: Cloning project <> - command: git clone --depth 1 https://github.com/cypress-io/<>.git /tmp/<> + - clone-repo-and-checkout-release-branch: + repo: <> - when: - condition: << parameters.pull_request_id >> + condition: <> steps: - run: - name: Check out PR << parameters.pull_request_id >> + name: Check out PR <> working_directory: /tmp/<> command: | - git fetch origin pull/<< parameters.pull_request_id >>/head:pr-<< parameters.pull_request_id >> - git checkout pr-<< parameters.pull_request_id >> + git fetch origin pull/<>/head:pr-<> + git checkout pr-<> git log -n 2 - run: command: npm install @@ -266,42 +280,42 @@ commands: command: npm start --if-present background: true - when: - condition: << parameters.folder >> + condition: <> steps: - when: - condition: << parameters.browser >> + condition: <> steps: - run: - name: Run tests using browser "<< parameters.browser >>" - working_directory: /tmp/<>/<< parameters.folder >> + name: Run tests using browser "<>" + working_directory: /tmp/<>/<> command: | <> -- --browser <> - unless: - condition: << parameters.browser >> + condition: <> steps: - run: name: Run tests using command - working_directory: /tmp/<>/<< parameters.folder >> + working_directory: /tmp/<>/<> command: <> - store_artifacts: name: screenshots - path: /tmp/<>/<< parameters.folder >>/cypress/screenshots + path: /tmp/<>/<>/cypress/screenshots - store_artifacts: name: videos - path: /tmp/<>/<< parameters.folder >>/cypress/videos + path: /tmp/<>/<>/cypress/videos - unless: - condition: << parameters.folder >> + condition: <> steps: - when: - condition: << parameters.browser >> + condition: <> steps: - run: - name: Run tests using browser "<< parameters.browser >>" + name: Run tests using browser "<>" working_directory: /tmp/<> command: <> -- --browser <> - unless: - condition: << parameters.browser >> + condition: <> steps: - run: name: Run tests using command @@ -904,45 +918,39 @@ jobs: test-kitchensink: <<: *defaults steps: - - attach_workspace: - at: ~/ - - run: - name: Cloning test project - command: git clone https://github.com/cypress-io/cypress-example-kitchensink.git /tmp/repo + - clone-repo-and-checkout-release-branch: + repo: cypress-example-kitchensink - run: name: Install prod dependencies command: yarn --production - working_directory: /tmp/repo + working_directory: /tmp/cypress-example-kitchensink - run: name: Example server command: yarn start - working_directory: /tmp/repo + working_directory: /tmp/cypress-example-kitchensink background: true - run: name: Run Kitchensink example project - command: yarn cypress:run --project /tmp/repo + command: yarn cypress:run --project /tmp/cypress-example-kitchensink - store_artifacts: - path: /tmp/repo/cypress/screenshots + path: /tmp/cypress-example-kitchensink/cypress/screenshots - store_artifacts: - path: /tmp/repo/cypress/videos + path: /tmp/cypress-example-kitchensink/cypress/videos - store-npm-logs "test-kitchensink-against-staging": <<: *defaults steps: - - attach_workspace: - at: ~/ - - run: - name: Cloning test project - command: git clone https://github.com/cypress-io/cypress-example-kitchensink.git /tmp/repo + - clone-repo-and-checkout-release-branch: + repo: cypress-example-kitchensink - run: name: Install prod dependencies command: yarn --production - working_directory: /tmp/repo + working_directory: /tmp/cypress-example-kitchensink - run: name: Example server command: yarn start - working_directory: /tmp/repo + working_directory: /tmp/cypress-example-kitchensink background: true - run: name: Run Kitchensink example project @@ -951,24 +959,21 @@ jobs: CYPRESS_RECORD_KEY=$TEST_KITCHENSINK_RECORD_KEY \ CYPRESS_INTERNAL_ENV=staging \ CYPRESS_video=false \ - yarn cypress:run --project /tmp/repo --record + yarn cypress:run --project /tmp/cypress-example-kitchensink --record - store-npm-logs "test-against-staging": <<: *defaults steps: - - attach_workspace: - at: ~/ - - run: - name: Cloning test project - command: git clone https://github.com/cypress-io/cypress-test-tiny.git /tmp/repo + - clone-repo-and-checkout-release-branch: + repo: cypress-test-tiny - run: name: Run test project command: | CYPRESS_PROJECT_ID=$TEST_TINY_PROJECT_ID \ CYPRESS_RECORD_KEY=$TEST_TINY_RECORD_KEY \ CYPRESS_INTERNAL_ENV=staging \ - yarn cypress:run --project /tmp/repo --record + yarn cypress:run --project /tmp/cypress-example-kitchensink --record - store-npm-logs build-npm-package: @@ -1164,16 +1169,16 @@ jobs: # make sure we have cypress.zip received - run: ls -l - run: ls -l cypress.zip cypress.tgz - - run: mkdir << parameters.wd >> + - run: mkdir <> - run: node --version - run: npm --version - run: name: Create new NPM package ⚗️ - working_directory: << parameters.wd >> + working_directory: <> command: npm init -y - run: name: Install dependencies 📦 - working_directory: << parameters.wd >> + working_directory: <> environment: CYPRESS_INSTALL_BINARY: /root/cypress/cypress.zip # let's install Cypress, Jest and any other package that might conflict @@ -1183,7 +1188,7 @@ jobs: typescript jest @types/jest enzyme @types/enzyme - run: name: Test types clash ⚔️ - working_directory: << parameters.wd >> + working_directory: <> command: | echo "console.log('hello world')" > hello.ts npx tsc hello.ts --noEmit @@ -1207,16 +1212,16 @@ jobs: # make sure we have cypress.zip received - run: ls -l - run: ls -l cypress.zip cypress.tgz - - run: mkdir << parameters.wd >> + - run: mkdir <> - run: node --version - run: npm --version - run: name: Create new NPM package ⚗️ - working_directory: << parameters.wd >> + working_directory: <> command: npm init -y - run: name: Install dependencies 📦 - working_directory: << parameters.wd >> + working_directory: <> environment: CYPRESS_INSTALL_BINARY: /root/cypress/cypress.zip # let's install Cypress, Jest and any other package that might conflict @@ -1226,7 +1231,7 @@ jobs: typescript jest @types/jest enzyme @types/enzyme - run: name: Scaffold and test examples 🏗 - working_directory: << parameters.wd >> + working_directory: <> environment: CYPRESS_INTERNAL_FORCE_SCAFFOLD: "1" command: | @@ -1250,27 +1255,27 @@ jobs: # make sure we have cypress.zip received - run: ls -l - run: ls -l cypress.zip cypress.tgz - - run: mkdir << parameters.wd >> + - run: mkdir <> - run: node --version - run: npm --version - run: name: Create new NPM package ⚗️ - working_directory: << parameters.wd >> + working_directory: <> command: npm init -y - run: name: Install dependencies 📦 - working_directory: << parameters.wd >> + working_directory: <> environment: CYPRESS_INSTALL_BINARY: /root/cypress/cypress.zip command: | npm install /root/cypress/cypress.tgz typescript - run: name: Scaffold full TypeScript project 🏗 - working_directory: << parameters.wd >> + working_directory: <> command: npx @bahmutov/cly@1 init --typescript - run: name: Run project tests 🗳 - working_directory: << parameters.wd >> + working_directory: <> command: npx cypress run # install NPM + binary zip and run against staging API @@ -1282,9 +1287,8 @@ jobs: - run: ls -l # make sure we have the binary and NPM package - run: ls -l cypress.zip cypress.tgz - - run: - name: Cloning test project - command: git clone https://github.com/cypress-io/cypress-test-tiny.git /tmp/cypress-test-tiny + - clone-repo-and-checkout-release-branch: + repo: cypress-test-tiny - run: name: Install Cypress working_directory: /tmp/cypress-test-tiny diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index cb4adf2ca055..affe28640cd6 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2130,7 +2130,7 @@ declare namespace Cypress { type Agent = SinonSpyAgent & T interface CookieDefaults { - whitelist: string | string[] | RegExp | ((cookie: any) => boolean) + preserve: string | string[] | RegExp | ((cookie: any) => boolean) } interface Failable { @@ -2616,7 +2616,7 @@ declare namespace Cypress { enable: boolean force404: boolean urlMatchingOptions: object - whitelist(xhr: Request): void + ignore(xhr: Request): void onAnyRequest(route: RouteOptions, proxy: any): void onAnyResponse(route: RouteOptions, proxy: any): void onAnyAbort(route: RouteOptions, proxy: any): void @@ -4806,7 +4806,7 @@ declare namespace Cypress { interface Server extends RouteOptions { enable: boolean - whitelist: (xhr: any) => boolean + ignore: (xhr: any) => boolean } interface Viewport { diff --git a/cli/types/tests/kitchen-sink.ts b/cli/types/tests/kitchen-sink.ts index ad0d299b102e..ae6ae2b5c9d8 100644 --- a/cli/types/tests/kitchen-sink.ts +++ b/cli/types/tests/kitchen-sink.ts @@ -35,7 +35,7 @@ cy.visit('https://www.acme.com/', { const serverOptions: Partial = { delay: 100, - whitelist: () => true + ignore: () => true } cy.server(serverOptions) diff --git a/packages/driver/cypress/integration/commands/cookies_spec.js b/packages/driver/cypress/integration/commands/cookies_spec.js index 6d2092626075..4cd21b40b484 100644 --- a/packages/driver/cypress/integration/commands/cookies_spec.js +++ b/packages/driver/cypress/integration/commands/cookies_spec.js @@ -1246,4 +1246,18 @@ describe('src/cy/commands/cookies', () => { }) }) }) + + context('Cypress.cookies.defaults', () => { + it('throws error on use of renamed whitelist option', (done) => { + cy.on('fail', (err) => { + expect(err.message).to.include('`Cypress.Cookies.defaults` `whitelist` option has been renamed to `preserve`. Please rename `whitelist` to `preserve`.') + + done() + }) + + Cypress.Cookies.defaults({ + whitelist: 'session_id', + }) + }) + }) }) diff --git a/packages/driver/cypress/integration/commands/xhr_spec.js b/packages/driver/cypress/integration/commands/xhr_spec.js index 1c7cd99a12ca..8bf33c750237 100644 --- a/packages/driver/cypress/integration/commands/xhr_spec.js +++ b/packages/driver/cypress/integration/commands/xhr_spec.js @@ -305,39 +305,6 @@ describe('src/cy/commands/xhr', () => { }) }) - // FIXME: I have no idea why this is skipped, this test is rly old - describe.skip('filtering requests', () => { - beforeEach(() => { - cy.server() - }) - - const extensions = { - html: 'ajax html', - js: '{foo: "bar"}', - css: 'body {}', - } - - _.each(extensions, (val, ext) => { - it(`filters out non ajax requests by default for extension: .${ext}`, (done) => { - cy.state('window').$.get(`/fixtures/app.${ext}`).done((res) => { - expect(res).to.eq(val) - - done() - }) - }) - }) - - it('can disable default filtering', (done) => { - // this should throw since it should return 404 when no - // route matches it - cy.server({ ignore: false }).window().then((w) => { - Promise.resolve(w.$.get('/fixtures/app.html')).catch(() => { - done() - }) - }) - }) - }) - describe('url rewriting', () => { it('has a FQDN absolute-relative url', () => { cy @@ -1139,6 +1106,14 @@ describe('src/cy/commands/xhr', () => { }) }) + it('sets ignore as function by default', () => { + cy.server() + cy.route('*', {}) + .then(() => { + expect(cy.state('server').getRoutes()[0].ignore).to.be.a('function') + }) + }) + it('passes down options.delay to routes', () => { cy .server({ delay: 100 }) @@ -1243,110 +1218,6 @@ describe('src/cy/commands/xhr', () => { }) }) - // FIXME: I have no idea why this is skipped, this test is rly old - context.skip('#server', () => { - beforeEach(function () { - const defaults = { - ignore: true, - respond: true, - delay: 10, - beforeRequest () {}, - afterResponse () {}, - onAbort () {}, - onError () {}, - onFilter () {}, - } - - this.options = (obj) => { - return _.extend(obj, defaults) - } - - this.create = cy.spy(this.Cypress.Server, 'create') - }) - - it('can accept an onRequest and onResponse callback', function (done) { - const onRequest = () => {} - const onResponse = () => {} - - cy.on('end', () => { - expect(this.create.getCall(0).args[1]).to.have.keys(_.keys(this.options({ onRequest, onResponse }))) - - done() - }) - - cy.server(onRequest, onResponse) - }) - - it('can accept onRequest and onRespond through options', function (done) { - const onRequest = () => {} - const onResponse = () => {} - - cy.on('end', () => { - expect(this.create.getCall(0).args[1]).to.have.keys(_.keys(this.options({ onRequest, onResponse }))) - - done() - }) - - cy.server({ onRequest, onResponse }) - }) - - describe('without sinon present', () => { - beforeEach(() => { - // force us to start from blank window - cy.state('$autIframe').prop('src', 'about:blank') - }) - - it('can start server with no errors', () => { - cy - .server() - .visit('http://localhost:3500/fixtures/sinon.html') - }) - - it('can add routes with no errors', () => { - cy - .server() - .route(/foo/, {}) - .visit('http://localhost:3500/fixtures/sinon.html') - }) - - it('routes xhr requests', () => { - cy - .server() - .route(/foo/, { foo: 'bar' }) - .visit('http://localhost:3500/fixtures/sinon.html') - .window().then((w) => { - return w.$.get('/foo') - }) - .then((resp) => { - expect(resp).to.deep.eq({ foo: 'bar' }) - }) - }) - - it('works with aliases', () => { - cy - .server() - .route(/foo/, { foo: 'bar' }).as('getFoo') - .visit('http://localhost:3500/fixtures/sinon.html') - .window().then((w) => { - return w.$.get('/foo') - }) - .wait('@getFoo').then((xhr) => { - expect(xhr.responseText).to.eq(JSON.stringify({ foo: 'bar' })) - }) - }) - - it('prevents XHR\'s from going out from sinon.html', () => { - cy - .server() - .route(/bar/, { bar: 'baz' }).as('getBar') - .visit('http://localhost:3500/fixtures/sinon.html') - .wait('@getBar').then((xhr) => { - expect(xhr.responseText).to.eq(JSON.stringify({ bar: 'baz' })) - }) - }) - }) - }) - context('#route', () => { beforeEach(function () { this.expectOptionsToBe = (opts) => { @@ -1909,6 +1780,16 @@ describe('src/cy/commands/xhr', () => { cy.route() }) + it('throws on use of whitelist option', (done) => { + cy.on('fail', (err) => { + expect(err.message).to.include('The `cy.server()` `whitelist` option has been renamed to `ignore`. Please rename `whitelist` to `ignore`.') + + done() + }) + + cy.server({ whitelist: () => { } }) + }) + it('url must be a string or regexp', (done) => { cy.on('fail', (err) => { expect(err.message).to.include('`cy.route()` was called with an invalid `url`. `url` must be either a string or regular expression.') @@ -2285,8 +2166,8 @@ describe('src/cy/commands/xhr', () => { }) }) - describe('whitelisting', () => { - it('does not send back 404s on whitelisted routes', () => { + describe('ignored routes', () => { + it('does not send back 404s on allowed routes', () => { cy .server() .window().then((win) => { @@ -2298,7 +2179,7 @@ describe('src/cy/commands/xhr', () => { }) // https://github.com/cypress-io/cypress/issues/7280 - it('ignores query params when whitelisting routes', () => { + it('ignores query params when filtering routes', () => { cy.server() cy.route(/url-with-query-param/, { foo: 'bar' }).as('getQueryParam') cy.window().then((win) => { @@ -2312,7 +2193,7 @@ describe('src/cy/commands/xhr', () => { }) // https://github.com/cypress-io/cypress/issues/7280 - it('ignores hashes when whitelisting routes', () => { + it('ignores hashes when filtering routes', () => { cy.server() cy.route(/url-with-hash/, { foo: 'bar' }).as('getHash') cy.window().then((win) => { @@ -2324,6 +2205,30 @@ describe('src/cy/commands/xhr', () => { cy.wait('@getHash').its('response.body') .should('deep.equal', { foo: 'bar' }) }) + + it('overrides ignoring resources when passed as option', () => { + cy.server({ ignore: () => false }) + cy.route('app.js', { foo: 'bar' }).as('getJSResource') + cy.route('index.html', '').as('getHTMLResource') + cy.route('style.css', 'body: {color: red;}').as('getCSSResource') + cy.window().then((win) => { + win.$.get('/fixtures/app.js') + win.$.get('/fixtures/style.css') + + return win.$.get('/fixtures/index.html') + }) + + // normally these resources would be ignored + // but overwriting ignore to return false allows all resources + cy.wait('@getJSResource').its('response.body') + .should('deep.equal', { foo: 'bar' }) + + cy.wait('@getHTMLResource').its('response.body') + .should('deep.equal', '') + + cy.wait('@getCSSResource').its('response.body') + .should('deep.equal', 'body: {color: red;}') + }) }) describe('route setup', () => { diff --git a/packages/driver/src/cy/commands/cookies.js b/packages/driver/src/cy/commands/cookies.js index 51e6d1f9ee0a..85c3170b7b30 100644 --- a/packages/driver/src/cy/commands/cookies.js +++ b/packages/driver/src/cy/commands/cookies.js @@ -95,7 +95,7 @@ module.exports = function (Commands, Cypress, cy, state, config) { return resp } - // iterate over all of these and ensure none are whitelisted + // iterate over all of these and ensure none are allowed // or preserved const cookies = Cypress.Cookies.getClearableCookies(resp) diff --git a/packages/driver/src/cy/keyboard.ts b/packages/driver/src/cy/keyboard.ts index 6f951fb07de6..3552a3da6d38 100644 --- a/packages/driver/src/cy/keyboard.ts +++ b/packages/driver/src/cy/keyboard.ts @@ -263,7 +263,7 @@ const shouldUpdateValue = (el: HTMLElement, key: KeyDetails, options: typeOption if (!(numberRe.test(potentialValue))) { debug('skipping inserting value since number input would be invalid', key.text, potentialValue) - // when typing in a number input, only certain whitelisted chars will insert text + // when typing in a number input, only certain allowed chars will insert text if (!key.text.match(isValidNumberInputChar)) { // https://github.com/cypress-io/cypress/issues/6055 // Should not remove old valid values when a new one is not a valid number char, just dismiss it with return diff --git a/packages/driver/src/cypress/cookies.js b/packages/driver/src/cypress/cookies.js index 01b92e441743..76d83d4fa1e8 100644 --- a/packages/driver/src/cypress/cookies.js +++ b/packages/driver/src/cypress/cookies.js @@ -9,7 +9,13 @@ let isDebuggingVerbose = false const preserved = {} const defaults = { - whitelist: null, + preserve: null, +} + +const warnOnWhitelistRenamed = (obj, type) => { + if (obj.whitelist) { + return $errUtils.throwErrByPath('cookies.whitelist_renamed', { args: { type } }) + } } const $Cookies = (namespace, domain) => { @@ -17,8 +23,8 @@ const $Cookies = (namespace, domain) => { return _.startsWith(name, namespace) } - const isWhitelisted = (cookie) => { - const w = defaults.whitelist + const isAllowed = (cookie) => { + const w = defaults.preserve if (w) { if (_.isString(w)) { @@ -76,7 +82,7 @@ const $Cookies = (namespace, domain) => { getClearableCookies (cookies = []) { return _.filter(cookies, (cookie) => { - return !isWhitelisted(cookie) && !removePreserved(cookie.name) + return !isAllowed(cookie) && !removePreserved(cookie.name) }) }, @@ -132,6 +138,8 @@ const $Cookies = (namespace, domain) => { }, defaults (obj = {}) { + warnOnWhitelistRenamed(obj, 'Cypress.Cookies.defaults') + // merge obj into defaults return _.extend(defaults, obj) }, diff --git a/packages/driver/src/cypress/error_messages.js b/packages/driver/src/cypress/error_messages.js index c5ae4f08f1cc..1f7d929017e5 100644 --- a/packages/driver/src/cypress/error_messages.js +++ b/packages/driver/src/cypress/error_messages.js @@ -301,6 +301,11 @@ module.exports = { - \`cy.clearCookie()\` - \`cy.clearCookies()\``, }, + whitelist_renamed (obj) { + return { + message: `\`${obj.type}\` \`whitelist\` option has been renamed to \`preserve\`. Please rename \`whitelist\` to \`preserve\`.`, + } + }, }, dom: { @@ -1286,6 +1291,7 @@ module.exports = { }, xhrurl_not_set: '`Server.options.xhrUrl` has not been set', unavailable: 'The XHR server is unavailable or missing. This should never happen and likely is a bug. Open an issue if you see this message.', + whitelist_renamed: `The ${cmd('server')} \`whitelist\` option has been renamed to \`ignore\`. Please rename \`whitelist\` to \`ignore\`.`, }, setCookie: { diff --git a/packages/driver/src/cypress/server.js b/packages/driver/src/cypress/server.js index 4610d8f91acf..097336edb019 100644 --- a/packages/driver/src/cypress/server.js +++ b/packages/driver/src/cypress/server.js @@ -65,7 +65,13 @@ const warnOnForce404Default = (obj) => { } } -const whitelist = (xhr) => { +const warnOnWhitelistRenamed = (obj, type) => { + if (obj.whitelist) { + return $errUtils.throwErrByPath('server.whitelist_renamed', { args: { type } }) + } +} + +const ignore = (xhr) => { const url = new URL(xhr.url) // https://github.com/cypress-io/cypress/issues/7280 @@ -74,7 +80,7 @@ const whitelist = (xhr) => { url.search = '' url.hash = '' - // whitelist if we're GET + looks like we're fetching regular resources + // allow if we're GET + looks like we're fetching regular resources return xhr.method === 'GET' && regularResourcesRe.test(url.href) } @@ -95,7 +101,7 @@ const serverDefaults = { urlMatchingOptions: { matchBase: true }, stripOrigin: _.identity, getUrlOptions: _.identity, - whitelist, // function whether to allow a request to go out (css/js/html/templates) etc + ignore, // function whether to allow a request to go out (css/js/html/templates) etc onOpen () {}, onSend () {}, onXhrAbort () {}, @@ -209,8 +215,8 @@ const create = (options = {}) => { return routes }, - isWhitelisted (xhr) { - return options.whitelist(xhr) + isIgnored (xhr) { + return options.ignore(xhr) }, shouldApplyStub (route) { @@ -257,9 +263,9 @@ const create = (options = {}) => { // return the 404 stub if we dont have any stubs // but we are stubbed - meaning we havent added any routes // but have started the server - // and this request shouldnt be whitelisted + // and this request shouldnt be allowed if (!routes.length && hasEnabledStubs && - options.force404 !== false && !server.isWhitelisted(xhr)) { + options.force404 !== false && !server.isIgnored(xhr)) { return get404Route() } @@ -268,8 +274,8 @@ const create = (options = {}) => { return nope() } - // bail if this xhr matches our whitelist - if (server.isWhitelisted(xhr)) { + // bail if this xhr matches our ignore list + if (server.isIgnored(xhr)) { return nope() } @@ -414,6 +420,7 @@ const create = (options = {}) => { set (obj) { warnOnStubDeprecation(obj, 'server') warnOnForce404Default(obj) + warnOnWhitelistRenamed(obj, 'server') // handle enable=true|false if (obj.enable != null) { @@ -648,8 +655,8 @@ const create = (options = {}) => { proxy._setRequestBody(requestBody) // log this out now since it's being sent officially - // unless its been whitelisted - if (!server.isWhitelisted(this)) { + // unless its not been ignored + if (!server.isIgnored(this)) { options.onSend(proxy, sendStack, route) } diff --git a/packages/example/bin/convert.js b/packages/example/bin/convert.js index 9e548350a03c..4936485cd923 100755 --- a/packages/example/bin/convert.js +++ b/packages/example/bin/convert.js @@ -24,6 +24,11 @@ function replaceStringsIn (file) { replace(eslintRe, "") replace("imgSrcToDataURL('/assets", "imgSrcToDataURL('https://example.cypress.io/assets") + // temporary for 5.0.0 + // TODO: remove this + replace("whitelist: 'session_id'", "preserve: 'session_id'") + replace("server.whitelist", "server.ignore") + fs.writeFile(file, str, function (err) { if (err) throw err diff --git a/packages/server/__snapshots__/2_cookies_spec.ts.js b/packages/server/__snapshots__/2_cookies_spec.ts.js index 7bc11a5d62cb..06024ba31b53 100644 --- a/packages/server/__snapshots__/2_cookies_spec.ts.js +++ b/packages/server/__snapshots__/2_cookies_spec.ts.js @@ -18,12 +18,12 @@ exports['e2e cookies with baseurl'] = ` cookies - with whitelist + with preserve ✓ can get all cookies ✓ resets cookies between tests correctly ✓ should be only two left now ✓ handles undefined cookies - without whitelist + without preserve ✓ sends set cookies to path ✓ handles expired cookies secure ✓ issue: #224 sets expired cookies between redirects @@ -122,12 +122,12 @@ exports['e2e cookies with no baseurl'] = ` cookies - with whitelist + with preserve ✓ can get all cookies ✓ resets cookies between tests correctly ✓ should be only two left now ✓ handles undefined cookies - without whitelist + without preserve ✓ sends cookies to localhost:2121 ✓ handles expired cookies secure ✓ issue: #224 sets expired cookies between redirects diff --git a/packages/server/lib/config.js b/packages/server/lib/config.js index 7f9cbd7596b7..a9dd933fa7dd 100644 --- a/packages/server/lib/config.js +++ b/packages/server/lib/config.js @@ -83,7 +83,7 @@ firefoxGcInterval\ `) // NOTE: If you add a config value, make sure to update the following -// - cli/types/index.d.ts (including whitelisted config options on TestOptions) +// - cli/types/index.d.ts (including allowed config options on TestOptions) // - cypress.schema.json // experimentalComponentTesting @@ -372,7 +372,7 @@ module.exports = { return _.includes(names, value) }, - whitelist (obj = {}) { + allowed (obj = {}) { const propertyNames = configKeys .concat(breakingConfigKeys) .concat(systemConfigKeys) @@ -427,7 +427,7 @@ module.exports = { debug('merged config with options, got %o', config) _ - .chain(this.whitelist(options)) + .chain(this.allowed(options)) .omit('env') .omit('browsers') .each((val, key) => { diff --git a/packages/server/lib/project.js b/packages/server/lib/project.js index d18a16676451..60d6e2b4504a 100644 --- a/packages/server/lib/project.js +++ b/packages/server/lib/project.js @@ -173,10 +173,10 @@ class Project extends EE { _initPlugins (cfg, options) { // only init plugins with the - // whitelisted config values to + // allowed config values to // prevent tampering with the // internals and breaking cypress - cfg = config.whitelist(cfg) + cfg = config.allowed(cfg) return plugins.init(cfg, { projectRoot: this.projectRoot, diff --git a/packages/server/lib/saved_state.js b/packages/server/lib/saved_state.js index dee0a7126890..4623b7d96d0c 100644 --- a/packages/server/lib/saved_state.js +++ b/packages/server/lib/saved_state.js @@ -9,7 +9,7 @@ const fs = require('./util/fs') const stateFiles = {} -const whitelist = ` +const allowed = ` appWidth appHeight appX @@ -64,7 +64,7 @@ const formStatePath = (projectRoot) => { }) } -const normalizeAndWhitelistSet = (set, key, value) => { +const normalizeAndAllowSet = (set, key, value) => { const valueObject = (() => { if (_.isString(key)) { const tmp = {} @@ -78,15 +78,15 @@ const normalizeAndWhitelistSet = (set, key, value) => { })() const invalidKeys = _.filter(_.keys(valueObject), (key) => { - return !_.includes(whitelist, key) + return !_.includes(allowed, key) }) if (invalidKeys.length) { // eslint-disable-next-line no-console - console.error(`WARNING: attempted to save state for non-whitelisted key(s): ${invalidKeys.join(', ')}. All keys must be whitelisted in server/lib/saved_state.js`) + console.error(`WARNING: attempted to save state for non-allowed key(s): ${invalidKeys.join(', ')}. All keys must be allowed in server/lib/saved_state.js`) } - return set(_.pick(valueObject, whitelist)) + return set(_.pick(valueObject, allowed)) } const create = (projectRoot, isTextTerminal) => { @@ -110,7 +110,7 @@ const create = (projectRoot, isTextTerminal) => { path: fullStatePath, }) - stateFile.set = _.wrap(stateFile.set.bind(stateFile), normalizeAndWhitelistSet) + stateFile.set = _.wrap(stateFile.set.bind(stateFile), normalizeAndAllowSet) stateFiles[fullStatePath] = stateFile diff --git a/packages/server/lib/server.js b/packages/server/lib/server.js index 02f7ccf206c5..440f39ba0956 100644 --- a/packages/server/lib/server.js +++ b/packages/server/lib/server.js @@ -28,7 +28,7 @@ const appData = require('./util/app_data') const statusCode = require('./util/status_code') const headersUtil = require('./util/headers') const allowDestroy = require('./util/server_destroy') -const { SocketWhitelist } = require('./util/socket_whitelist') +const { SocketAllowed } = require('./util/socket_allowed') const errors = require('./errors') const logger = require('./logger') const Socket = require('./socket') @@ -59,7 +59,7 @@ const _forceProxyMiddleware = function (clientRoute) { const trimmedUrl = _.trimEnd(req.proxiedUrl, '/') if (_isNonProxiedRequest(req) && !ALLOWED_PROXY_BYPASS_URLS.includes(trimmedUrl) && (trimmedUrl !== trimmedClientRoute)) { - // this request is non-proxied and non-whitelisted, redirect to the runner error page + // this request is non-proxied and non-allowed, redirect to the runner error page return res.redirect(clientRoute) } @@ -115,7 +115,7 @@ class Server { return new Server() } - this._socketWhitelist = new SocketWhitelist() + this._socketAllowed = new SocketAllowed() this._request = null this._middleware = null this._server = null @@ -278,7 +278,7 @@ class Server { this._server.on('connect', (req, socket, head) => { debug('Got CONNECT request from %s', req.url) - socket.once('upstream-connected', this._socketWhitelist.add) + socket.once('upstream-connected', this._socketAllowed.add) return this._httpsProxy.connect(req, socket, head, { onDirectConnection: (req) => { @@ -761,7 +761,7 @@ class Server { let host; let remoteOrigin if (req.url.startsWith(socketIoRoute)) { - if (!this._socketWhitelist.isRequestWhitelisted(req)) { + if (!this._socketAllowed.isRequestAllowed(req)) { socket.write('HTTP/1.1 400 Bad Request\r\n\r\nRequest not made via a Cypress-launched browser.') socket.end() } diff --git a/packages/server/lib/util/args.js b/packages/server/lib/util/args.js index 43184b043ea6..cabcdebf8546 100644 --- a/packages/server/lib/util/args.js +++ b/packages/server/lib/util/args.js @@ -13,7 +13,7 @@ const nestedObjectsInCurlyBracesRe = /\{(.+?)\}/g const nestedArraysInSquareBracketsRe = /\[(.+?)\]/g const everythingAfterFirstEqualRe = /=(.*)/ -const whitelist = 'appPath apiKey browser ci ciBuildId clearLogs config configFile cwd env execPath exit exitWithCode generateKey getKey group headed inspectBrk key logs mode outputPath parallel ping port project proxySource quiet record reporter reporterOptions returnPkg runMode runProject smokeTest spec tag updating version'.split(' ') +const allowList = 'appPath apiKey browser ci ciBuildId clearLogs config configFile cwd env execPath exit exitWithCode generateKey getKey group headed inspectBrk key logs mode outputPath parallel ping port project proxySource quiet record reporter reporterOptions returnPkg runMode runProject smokeTest spec tag updating version'.split(' ') // returns true if the given string has double quote character " // only at the last position. const hasStrayEndQuote = (s) => { @@ -189,14 +189,14 @@ module.exports = { alias, }) - const whitelisted = _.pick(argv, whitelist) + const allowed = _.pick(argv, allowList) // were we invoked from the CLI or directly? const invokedFromCli = Boolean(options.cwd) options = _ .chain(options) - .defaults(whitelisted) + .defaults(allowed) .omit(_.keys(alias)) // remove aliases .extend({ invokedFromCli }) .defaults({ @@ -320,11 +320,11 @@ module.exports = { toArray (obj = {}) { // goes in reverse, takes an object // and converts to an array by picking - // only the whitelisted properties and + // only the allowed properties and // mapping them to include the argument return _ .chain(obj) - .pick(...whitelist) + .pick(...allowList) .mapValues((val, key) => { return `--${key}=${stringify(val)}` }).values() diff --git a/packages/server/lib/util/socket_allowed.ts b/packages/server/lib/util/socket_allowed.ts new file mode 100644 index 000000000000..46ed8a32a386 --- /dev/null +++ b/packages/server/lib/util/socket_allowed.ts @@ -0,0 +1,46 @@ +import _ from 'lodash' +import Debug from 'debug' +import net from 'net' +import { Request } from 'express' + +const debug = Debug('cypress:server:util:socket_allowed') + +/** + * Utility to validate incoming, local socket connections against a list of + * expected client TCP ports. + */ +export class SocketAllowed { + allowedLocalPorts: number[] = [] + + /** + * Add a socket to the allowed list. + */ + add = (socket: net.Socket) => { + const { localPort } = socket + + debug('allowing socket %o', { localPort }) + this.allowedLocalPorts.push(localPort) + + socket.once('close', () => { + debug('allowed socket closed, removing %o', { localPort }) + this._remove(socket) + }) + } + + _remove (socket: net.Socket) { + _.pull(this.allowedLocalPorts, socket.localPort) + } + + /** + * Is this socket that this request originated allowed? + */ + isRequestAllowed (req: Request) { + const { remotePort, remoteAddress } = req.socket + const isAllowed = this.allowedLocalPorts.includes(remotePort!) + && ['127.0.0.1', '::1'].includes(remoteAddress!) + + debug('is incoming request allowed? %o', { isAllowed, reqUrl: req.url, remotePort, remoteAddress }) + + return isAllowed + } +} diff --git a/packages/server/lib/util/socket_whitelist.ts b/packages/server/lib/util/socket_whitelist.ts deleted file mode 100644 index 421426d417b6..000000000000 --- a/packages/server/lib/util/socket_whitelist.ts +++ /dev/null @@ -1,46 +0,0 @@ -import _ from 'lodash' -import Debug from 'debug' -import net from 'net' -import { Request } from 'express' - -const debug = Debug('cypress:server:util:socket_whitelist') - -/** - * Utility to validate incoming, local socket connections against a list of - * expected client TCP ports. - */ -export class SocketWhitelist { - whitelistedLocalPorts: number[] = [] - - /** - * Add a socket to the whitelist. - */ - add = (socket: net.Socket) => { - const { localPort } = socket - - debug('whitelisting socket %o', { localPort }) - this.whitelistedLocalPorts.push(localPort) - - socket.once('close', () => { - debug('whitelisted socket closed, removing %o', { localPort }) - this._remove(socket) - }) - } - - _remove (socket: net.Socket) { - _.pull(this.whitelistedLocalPorts, socket.localPort) - } - - /** - * Is this socket that this request originated from whitelisted? - */ - isRequestWhitelisted (req: Request) { - const { remotePort, remoteAddress } = req.socket - const isWhitelisted = this.whitelistedLocalPorts.includes(remotePort!) - && ['127.0.0.1', '::1'].includes(remoteAddress!) - - debug('is incoming request whitelisted? %o', { isWhitelisted, reqUrl: req.url, remotePort, remoteAddress }) - - return isWhitelisted - } -} diff --git a/packages/server/test/support/fixtures/projects/cookies/cypress/integration/app_spec.coffee b/packages/server/test/support/fixtures/projects/cookies/cypress/integration/app_spec.coffee index 32bdcc41fcd0..b80abfbddd1c 100644 --- a/packages/server/test/support/fixtures/projects/cookies/cypress/integration/app_spec.coffee +++ b/packages/server/test/support/fixtures/projects/cookies/cypress/integration/app_spec.coffee @@ -1,5 +1,5 @@ Cypress.Cookies.defaults({ - whitelist: "foo1" + preserve: "foo1" }) describe "Cookies", -> @@ -84,3 +84,4 @@ describe "Cookies", -> domain: "brian.dev.local" }) .its("body").should("deep.eq", {wow: "bob"}) + \ No newline at end of file diff --git a/packages/server/test/support/fixtures/projects/e2e/cypress/integration/cookies_spec_baseurl.coffee b/packages/server/test/support/fixtures/projects/e2e/cypress/integration/cookies_spec_baseurl.coffee index a712c2597fe6..bea74bb33864 100644 --- a/packages/server/test/support/fixtures/projects/e2e/cypress/integration/cookies_spec_baseurl.coffee +++ b/packages/server/test/support/fixtures/projects/e2e/cypress/integration/cookies_spec_baseurl.coffee @@ -35,10 +35,10 @@ describe "cookies", -> beforeEach -> cy.wrap({foo: "bar"}) - context "with whitelist", -> + context "with preserve", -> before -> Cypress.Cookies.defaults({ - whitelist: "foo1" + preserve: "foo1" }) it "can get all cookies", -> @@ -104,10 +104,10 @@ describe "cookies", -> it "handles undefined cookies", -> cy.visit("/cookieWithNoName") - context "without whitelist", -> + context "without preserve", -> before -> Cypress.Cookies.defaults({ - whitelist: [] + preserve: [] }) it "sends set cookies to path", -> diff --git a/packages/server/test/support/fixtures/projects/e2e/cypress/integration/cookies_spec_no_baseurl.coffee b/packages/server/test/support/fixtures/projects/e2e/cypress/integration/cookies_spec_no_baseurl.coffee index 9fd762d558f9..7fdba2822b5f 100644 --- a/packages/server/test/support/fixtures/projects/e2e/cypress/integration/cookies_spec_no_baseurl.coffee +++ b/packages/server/test/support/fixtures/projects/e2e/cypress/integration/cookies_spec_no_baseurl.coffee @@ -5,10 +5,10 @@ describe "cookies", -> beforeEach -> cy.wrap({foo: "bar"}) - context "with whitelist", -> + context "with preserve", -> before -> Cypress.Cookies.defaults({ - whitelist: "foo1" + preserve: "foo1" }) it "can get all cookies", -> @@ -79,10 +79,10 @@ describe "cookies", -> it "handles undefined cookies", -> cy.visit("#{httpUrl}/cookieWithNoName") - context "without whitelist", -> + context "without preserve", -> before -> Cypress.Cookies.defaults({ - whitelist: [] + preserve: [] }) it "sends cookies to localhost:2121", -> diff --git a/packages/server/test/unit/args_spec.js b/packages/server/test/unit/args_spec.js index d7be0988e085..c7d896434b84 100644 --- a/packages/server/test/unit/args_spec.js +++ b/packages/server/test/unit/args_spec.js @@ -270,7 +270,7 @@ describe('lib/util/args', () => { expect(options.config).to.deep.eq(config) }) - it('whitelists config properties', function () { + it('allows config properties', function () { const options = this.setup('--config', 'foo=bar,port=1111,supportFile=path/to/support_file') expect(options.config.port).to.eq(1111) diff --git a/packages/server/test/unit/saved_state_spec.js b/packages/server/test/unit/saved_state_spec.js index 993e74248975..aadce5110414 100644 --- a/packages/server/test/unit/saved_state_spec.js +++ b/packages/server/test/unit/saved_state_spec.js @@ -62,7 +62,7 @@ describe('lib/saved_state', () => { }) }) - it('only saves whitelisted keys', () => { + it('only saves allowed keys', () => { return savedState.create() .then((state) => { return state.set({ foo: 'bar', appWidth: 20 }) @@ -81,7 +81,7 @@ describe('lib/saved_state', () => { .then((state) => { return state.set({ foo: 'bar', baz: 'qux' }) }).then(() => { - expect(console.error).to.be.calledWith('WARNING: attempted to save state for non-whitelisted key(s): foo, baz. All keys must be whitelisted in server/lib/saved_state.js') + expect(console.error).to.be.calledWith('WARNING: attempted to save state for non-allowed key(s): foo, baz. All keys must be allowed in server/lib/saved_state.js') }) }) }) diff --git a/packages/server/test/unit/screenshots_spec.js b/packages/server/test/unit/screenshots_spec.js index e14c68da4c96..b4b5aadce252 100644 --- a/packages/server/test/unit/screenshots_spec.js +++ b/packages/server/test/unit/screenshots_spec.js @@ -679,7 +679,7 @@ describe('lib/screenshots', () => { return sinon.stub(plugins, 'execute') }) - it('resolves whitelisted details if no after:screenshot plugin registered', function () { + it('resolves allowed details if no after:screenshot plugin registered', function () { plugins.has.returns(false) return screenshots.afterScreenshot(this.data, this.details).then((result) => { diff --git a/packages/server/test/unit/server_spec.js b/packages/server/test/unit/server_spec.js index fdc42b121614..c0025d36bbe0 100644 --- a/packages/server/test/unit/server_spec.js +++ b/packages/server/test/unit/server_spec.js @@ -338,7 +338,7 @@ describe('lib/server', () => { remoteAddress: '127.0.0.1', } - this.server._socketWhitelist.add({ + this.server._socketAllowed.add({ localPort: socket.remotePort, once: _.noop, }) diff --git a/packages/server/test/unit/util/socket_allowed_spec.ts b/packages/server/test/unit/util/socket_allowed_spec.ts new file mode 100644 index 000000000000..836e38ca8093 --- /dev/null +++ b/packages/server/test/unit/util/socket_allowed_spec.ts @@ -0,0 +1,42 @@ +import '../../spec_helper' + +import { expect } from 'chai' +import { Request } from 'express' +import { SocketAllowed } from '../../../lib/util/socket_allowed' +import { EventEmitter } from 'events' +import { Socket } from 'net' + +describe('lib/util/socket_allowed', function () { + let sw: SocketAllowed + + beforeEach(() => { + sw = new SocketAllowed() + }) + + context('#add', () => { + it('adds localPort to allowed list and removes it when closed', () => { + const socket = new EventEmitter as Socket + + // @ts-ignore readonly + socket.localPort = 12345 + + const req = { + socket: { + remotePort: socket.localPort, + remoteAddress: '127.0.0.1', + }, + } as Request + + expect(sw.allowedLocalPorts).to.deep.eq([]) + expect(sw.isRequestAllowed(req)).to.be.false + + sw.add(socket) + expect(sw.allowedLocalPorts).to.deep.eq([socket.localPort]) + expect(sw.isRequestAllowed(req)).to.be.true + + socket.emit('close') + expect(sw.allowedLocalPorts).to.deep.eq([]) + expect(sw.isRequestAllowed(req)).to.be.false + }) + }) +}) diff --git a/packages/server/test/unit/util/socket_whitelist_spec.ts b/packages/server/test/unit/util/socket_whitelist_spec.ts deleted file mode 100644 index e8fa34642671..000000000000 --- a/packages/server/test/unit/util/socket_whitelist_spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import '../../spec_helper' - -import { expect } from 'chai' -import { Request } from 'express' -import { SocketWhitelist } from '../../../lib/util/socket_whitelist' -import { EventEmitter } from 'events' -import { Socket } from 'net' - -describe('lib/util/socket_whitelist', function () { - let sw: SocketWhitelist - - beforeEach(() => { - sw = new SocketWhitelist() - }) - - context('#add', () => { - it('adds localPort to whitelist and removes it when closed', () => { - const socket = new EventEmitter as Socket - - // @ts-ignore readonly - socket.localPort = 12345 - - const req = { - socket: { - remotePort: socket.localPort, - remoteAddress: '127.0.0.1', - }, - } as Request - - expect(sw.whitelistedLocalPorts).to.deep.eq([]) - expect(sw.isRequestWhitelisted(req)).to.be.false - - sw.add(socket) - expect(sw.whitelistedLocalPorts).to.deep.eq([socket.localPort]) - expect(sw.isRequestWhitelisted(req)).to.be.true - - socket.emit('close') - expect(sw.whitelistedLocalPorts).to.deep.eq([]) - expect(sw.isRequestWhitelisted(req)).to.be.false - }) - }) -})