UNPKG

@revoloo/cypress6

Version:

Cypress.io end to end testing tool

1,686 lines (1,426 loc) 136 kB
('../spec_helper') const _ = require('lodash') let r = require('@cypress/request') const rp = require('@cypress/request-promise') const compression = require('compression') const dns = require('dns') const express = require('express') const http = require('http') const path = require('path') const url = require('url') let zlib = require('zlib') const str = require('underscore.string') const evilDns = require('evil-dns') const Promise = require('bluebird') const httpsServer = require(`${root}../https-proxy/test/helpers/https_server`) const pkg = require('@packages/root') const SseStream = require('ssestream') const EventSource = require('eventsource') const config = require(`${root}lib/config`) const { ServerE2E } = require(`${root}lib/server-e2e`) const { ProjectE2E } = require(`${root}lib/project-e2e`) const Watchers = require(`${root}lib/watchers`) const pluginsModule = require(`${root}lib/plugins`) const preprocessor = require(`${root}lib/plugins/preprocessor`) const resolve = require(`${root}lib/util/resolve`) const { fs } = require(`${root}lib/util/fs`) const glob = require(`${root}lib/util/glob`) const CacheBuster = require(`${root}lib/util/cache_buster`) const Fixtures = require(`${root}test/support/helpers/fixtures`) zlib = Promise.promisifyAll(zlib) // force supertest-session to use promises provided in supertest const session = proxyquire('supertest-session', { supertest }) const absolutePathRegex = /"\/[^{}]*?\.projects/g let sourceMapRegex = /\n\/\/# sourceMappingURL\=.*/ const replaceAbsolutePaths = (content) => { return content.replace(absolutePathRegex, '"/<path-to-project>') } const removeWhitespace = function (c) { c = str.clean(c) c = str.lines(c).join(' ') return c } const cleanResponseBody = (body) => { return replaceAbsolutePaths(removeWhitespace(body)) } describe('Routes', () => { require('mocha-banner').register() beforeEach(function () { process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0' sinon.stub(CacheBuster, 'get').returns('-123') sinon.stub(ServerE2E.prototype, 'reset') sinon.stub(pluginsModule, 'has').returns(false) nock.enableNetConnect() this.setup = (initialUrl, obj = {}) => { if (_.isObject(initialUrl)) { obj = initialUrl initialUrl = null } if (!obj.projectRoot) { obj.projectRoot = '/foo/bar/' } // get all the config defaults // and allow us to override them // for each test return config.set(obj) .then((cfg) => { // use a jar for each test // but reset it automatically // between test const jar = rp.jar() this.r = function (options = {}, cb) { _.defaults(options, { proxy: this.proxy, jar, simple: false, followRedirect: false, resolveWithFullResponse: true, }) return r(options, cb) } // use a custom request promise // to automatically backfill these // options including our proxy this.rp = (options = {}) => { let targetUrl if (_.isString(options)) { targetUrl = options options = {} } _.defaults(options, { url: targetUrl, proxy: this.proxy, jar, simple: false, followRedirect: false, resolveWithFullResponse: true, }) return rp(options) } const open = () => { this.project = new ProjectE2E('/path/to/project-e2e') cfg.pluginsFile = false return Promise.all([ // open our https server httpsServer.start(8443), // and open our cypress server (this.server = new ServerE2E(new Watchers())), this.server.open(cfg, this.project) .spread(async (port) => { const automationStub = { use: () => { }, } await this.server.startWebsockets(automationStub, config, {}) if (initialUrl) { this.server._onDomainSet(initialUrl) } this.srv = this.server.getHttpServer() this.session = session(this.srv) this.proxy = `http://localhost:${port}` }), pluginsModule.init(cfg, { projectRoot: cfg.projectRoot, }), ]) } if (this.server) { return Promise.join( httpsServer.stop(), this.server.close(), ) .then(open) } return open() }) } }) afterEach(function () { evilDns.clear() nock.cleanAll() Fixtures.remove() this.session.destroy() preprocessor.close() this.project = null return Promise.join( this.server.close(), httpsServer.stop(), ) }) context('GET /', () => { beforeEach(function () { return this.setup() }) // this tests a situation where we open our browser in another browser // without proxy mode set it('redirects to config.clientRoute without a _remoteOrigin and without a proxy', function () { return this.rp({ url: this.proxy, proxy: null, }) .then((res) => { expect(res.statusCode).to.eq(302) expect(res.headers.location).to.eq('/__/') }) }) it('does not redirect with _remoteOrigin set', function () { return this.setup('http://www.github.com') .then(() => { nock(this.server._remoteOrigin) .get('/') .reply(200, '<html></html>', { 'Content-Type': 'text/html', }) return this.rp({ url: 'http://www.github.com/', headers: { 'Cookie': '__cypress.initial=true', }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.include('<html>') expect(res.body).to.include('document.domain = \'github.com\'') expect(res.body).to.include('</html>') }) }) }) it('does not redirect when visiting http site which isnt cypress server', function () { // this tests the 'default' state of cypress when a server // is instantiated. i noticed that before you do your first // cy.visit() that all http sites would redirect which is // incorrect. we only want the cypress port to redirect initially nock('http://www.momentjs.com') .get('/') .reply(200) return this.rp('http://www.momentjs.com/') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.headers.location).not.to.eq('/__/') }) }) it('proxies through https', function () { return this.setup('https://localhost:8443') .then(() => { return this.rp({ url: 'https://localhost:8443/', headers: { 'Accept': 'text/html, application/xhtml+xml, */*', }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).not.to.include('Cypress') expect(res.body).to.include('document.domain = \'localhost\'') expect(res.body).to.include('<body>https server</body>') }) }) }) }) context('GET /__', () => { beforeEach(function () { return this.setup({ projectName: 'foobarbaz' }) }) it('routes config.clientRoute to serve cypress client app html', function () { return this.rp('http://localhost:2020/__') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.match(/Runner.start/) }) }) it('sets title to projectName', function () { return this.rp('http://localhost:2020/__') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.include('<title>foobarbaz</title>') }) }) it('omits x-powered-by', function () { return this.rp('http://localhost:2020/__') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.headers['x-powered-by']).not.to.exist }) }) it('proxies through https', function () { return this.setup('https://localhost:8443') .then(() => { return this.rp('https://localhost:8443/__') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.match(/Runner.start\(.+\)/) }) }) }) it('clientRoute routes to \'not launched through Cypress\' without a proxy set', function () { return this.rp({ url: `${this.proxy}/__`, proxy: null, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.match(/This browser was not launched through Cypress\./) }) }) it('other URLs redirect to clientRoute without a proxy set', function () { // test something that isn't the clientRoute return this.rp({ url: `${this.proxy}/__cypress/xhrs/foo`, proxy: null, }) .then((res) => { expect(res.statusCode).to.eq(302) expect(res.headers['location']).to.eq('/__/') }) }) it('routes when baseUrl is set', function () { return this.setup({ baseUrl: 'http://localhost:9999/app' }) .then(() => { return this.rp('http://localhost:9999/__') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.match(/Runner.start/) }) }) }) it('sends Cypress.version', function () { return this.setup({ baseUrl: 'http://localhost:9999/app' }) .then(() => { return this.rp('http://localhost:9999/__') .then((res) => { expect(res.statusCode).to.eq(200) const base64Config = /Runner\.start\(.*, "(.*)"\)/.exec(res.body)[1] const configStr = Buffer.from(base64Config, 'base64').toString() expect(configStr).to.include('version') expect(configStr).to.include(pkg.version) }) }) }) }) context('GET /__cypress/runner/*', () => { beforeEach(function () { return this.setup('http://localhost:8443') }) it('can get cypress_runner.js', function () { return this.rp('http://localhost:8443/__cypress/runner/cypress_runner.js') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.match(/React/) }) }) it('can get cypress_runner.css', function () { return this.rp('http://localhost:8443/__cypress/runner/cypress_runner.css') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.match(/spec-iframe/) }) }) }) context('GET /__cypress/files', () => { beforeEach(() => { Fixtures.scaffold('todos') return Fixtures.scaffold('ids') }) describe('todos with specific configuration', () => { beforeEach(function () { return this.setup({ projectRoot: Fixtures.projectPath('todos'), config: { integrationFolder: 'tests', fixturesFolder: 'tests/_fixtures', supportFile: 'tests/_support/spec_helper.js', javascripts: ['tests/etc/**/*'], }, }) }) it('returns base json file path objects of only tests', function () { // this should omit any _fixture files, _support files and javascripts return glob(path.join(Fixtures.projectPath('todos'), 'tests', '_fixtures', '**', '*')) .then((files) => { // make sure there are fixtures in here! expect(files.length).to.be.gt(0) return glob(path.join(Fixtures.projectPath('todos'), 'tests', '_support', '**', '*')) .then((files) => { // make sure there are support files in here! expect(files.length).to.be.gt(0) return this.rp({ url: 'http://localhost:2020/__cypress/files', json: true, }) .then((res) => { expect(res.statusCode).to.eq(200) const { body, } = res expect(body.integration).to.have.length(4) // remove the absolute path key body.integration = _.map(body.integration, (obj) => { return _.pick(obj, 'name', 'relative') }) expect(res.body).to.deep.eq({ integration: [ { 'name': 'sub/a&b%c.js', 'relative': 'tests/sub/a&b%c.js', }, { name: 'sub/sub_test.coffee', relative: 'tests/sub/sub_test.coffee', }, { name: 'test1.js', relative: 'tests/test1.js', }, { name: 'test2.coffee', relative: 'tests/test2.coffee', }, ], }) }) }) }) }) }) describe('ids with regular configuration', () => { it('returns test files as json ignoring *.hot-update.js', function () { return this.setup({ projectRoot: Fixtures.projectPath('ids'), }) .then(() => { return this.rp({ url: 'http://localhost:2020/__cypress/files', json: true, }) .then((res) => { expect(res.statusCode).to.eq(200) const { body, } = res expect(body.integration).to.have.length(6) // remove the absolute path key body.integration = _.map(body.integration, (obj) => { return _.pick(obj, 'name', 'relative') }) expect(body).to.deep.eq({ integration: [ { name: 'bar.js', relative: 'cypress/integration/bar.js', }, { name: 'baz.js', relative: 'cypress/integration/baz.js', }, { name: 'dom.jsx', relative: 'cypress/integration/dom.jsx', }, { name: 'foo.coffee', relative: 'cypress/integration/foo.coffee', }, { name: 'nested/tmp.js', relative: 'cypress/integration/nested/tmp.js', }, { name: 'noop.coffee', relative: 'cypress/integration/noop.coffee', }, ], }) }) }) }) it('can ignore other files as well', function () { return this.setup({ projectRoot: Fixtures.projectPath('ids'), config: { ignoreTestFiles: ['**/bar.js', 'foo.coffee', '**/*.hot-update.js', '**/nested/*'], }, }) .then(() => { return this.rp({ url: 'http://localhost:2020/__cypress/files', json: true, }) .then((res) => { expect(res.statusCode).to.eq(200) const { body, } = res expect(body.integration).to.have.length(3) // remove the absolute path key body.integration = _.map(body.integration, (obj) => { return _.pick(obj, 'name', 'relative') }) expect(body).to.deep.eq({ integration: [ { name: 'baz.js', relative: 'cypress/integration/baz.js', }, { name: 'dom.jsx', relative: 'cypress/integration/dom.jsx', }, { name: 'noop.coffee', relative: 'cypress/integration/noop.coffee', }, ], }) }) }) }) }) }) context('GET /__cypress/tests', () => { describe('ids with typescript', () => { beforeEach(function () { Fixtures.scaffold('ids') return this.setup({ projectRoot: Fixtures.projectPath('ids'), }) }) it('processes foo.coffee spec', function () { return this.rp('http://localhost:2020/__cypress/tests?p=cypress/integration/foo.coffee') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.include('expect("foo.coffee")') }) }) it('processes dom.jsx spec', function () { return this.rp('http://localhost:2020/__cypress/tests?p=cypress/integration/baz.js') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.include('React.createElement(') }) }) it('serves error javascript file when the file is missing', function () { return this.rp('http://localhost:2020/__cypress/tests?p=does/not/exist.coffee') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.include('Cypress.action("spec:script:error", {') expect(res.body).to.include('Module not found') }) }) }) describe('ids without typescript', () => { beforeEach(function () { Fixtures.scaffold('ids') sinon.stub(resolve, 'typescript').callsFake(() => { return null }) return this.setup({ projectRoot: Fixtures.projectPath('ids'), }) }) it('processes foo.coffee spec', function () { return this.rp('http://localhost:2020/__cypress/tests?p=cypress/integration/foo.coffee') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.match(sourceMapRegex) expect(res.body).to.include('expect("foo.coffee")') }) }) it('processes dom.jsx spec', function () { return this.rp('http://localhost:2020/__cypress/tests?p=cypress/integration/baz.js') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.match(sourceMapRegex) expect(res.body).to.include('React.createElement(') }) }) it('serves error javascript file when the file is missing', function () { return this.rp('http://localhost:2020/__cypress/tests?p=does/not/exist.coffee') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.include('Cypress.action("spec:script:error", {') expect(res.body).to.include('Module not found') }) }) }) describe('failures', () => { beforeEach(function () { Fixtures.scaffold('failures') return this.setup({ projectRoot: Fixtures.projectPath('failures'), }) }) it('serves error javascript file when there\'s a syntax error', function () { return this.rp('http://localhost:2020/__cypress/tests?p=cypress/integration/syntax_error.js') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.include('Cypress.action("spec:script:error", {') expect(res.body).to.include('Unexpected token') }) }) }) describe('no-server', () => { beforeEach(function () { Fixtures.scaffold('no-server') return this.setup({ projectRoot: Fixtures.projectPath('no-server'), config: { integrationFolder: 'my-tests', javascripts: ['helpers/includes.js'], }, }) }) it('processes my-tests/test1.js spec', function () { return this.rp('http://localhost:2020/__cypress/tests?p=my-tests/test1.js') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.match(sourceMapRegex) expect(res.body).to.include(`expect('no-server')`) }) }) it('processes helpers/includes.js javascripts', function () { return this.rp('http://localhost:2020/__cypress/tests?p=helpers/includes.js') .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.match(sourceMapRegex) expect(res.body).to.include(`console.log('includes')`) }) }) }) }) context('ALL /__cypress/xhrs/*', () => { beforeEach(function () { return this.setup() }) describe('delay', () => { it('can set delay to 10ms', function () { const delay = sinon.spy(Promise, 'delay') return this.rp({ url: 'http://localhost:2020/__cypress/xhrs/users/1', headers: { 'x-cypress-delay': '10', }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(delay).to.be.calledWith(10) }) }) it('does not call Promise.delay when no delay', function () { const delay = sinon.spy(Promise, 'delay') return this.rp('http://localhost:2020/__cypress/xhrs/users/1') .then((res) => { expect(res.statusCode).to.eq(200) expect(delay).not.to.be.called }) }) }) describe('status', () => { it('can set status', function () { return this.rp({ url: 'http://localhost:2020/__cypress/xhrs/users/1', headers: { 'x-cypress-status': '401', }, }) .then((res) => { expect(res.statusCode).to.eq(401) }) }) }) describe('headers', () => { it('can set headers', function () { const headers = JSON.stringify({ 'x-token': '123', 'content-type': 'text/plain', }) return this.rp({ url: 'http://localhost:2020/__cypress/xhrs/users/1', headers: { 'x-cypress-headers': headers, }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.headers['content-type']).to.match(/text\/plain/) expect(res.headers['x-token']).to.eq('123') }) }) it('sets headers from response type', function () { const headers = JSON.stringify({ 'x-token': '123', }) return this.rp({ url: 'http://localhost:2020/__cypress/xhrs/users/1', json: true, headers: { 'x-cypress-headers': headers, 'x-cypress-response': JSON.stringify({ foo: 'bar' }), }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.headers['content-type']).to.match(/application\/json/) expect(res.headers['x-token']).to.eq('123') expect(res.body).to.deep.eq({ foo: 'bar' }) }) }) }) describe('response', () => { it('sets response to json', function () { return this.rp({ url: 'http://localhost:2020/__cypress/xhrs/users/1', json: true, headers: { 'x-cypress-response': JSON.stringify([1, 2, 3]), }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.headers['content-type']).to.match(/application\/json/) expect(res.body).to.deep.eq([1, 2, 3]) }) }) it('sets response to text/html', function () { return this.rp({ url: 'http://localhost:2020/__cypress/xhrs/users/1', headers: { 'x-cypress-response': '<html>foo</html>', }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.headers['content-type']).to.match(/text\/html/) expect(res.body).to.eq('<html>foo</html>') }) }) it('sets response to text/plain', function () { return this.rp({ url: 'http://localhost:2020/__cypress/xhrs/users/1', headers: { 'x-cypress-response': 'foobarbaz', }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.headers['content-type']).to.match(/text\/plain/) expect(res.body).to.eq('foobarbaz') }) }) it('sets response to text/plain on empty response', function () { return this.rp({ url: 'http://localhost:2020/__cypress/xhrs/users/1', headers: { 'x-cypress-response': '', }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.headers['content-type']).to.match(/text\/plain/) expect(res.body).to.eq('') }) }) it('decodes responses', function () { const response = encodeURI(JSON.stringify({ 'test': 'We’ll', })) return this.rp({ url: 'http://localhost:2020/__cypress/xhrs/users/1', json: true, headers: { 'x-cypress-response': response, }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.deep.eq({ test: 'We’ll' }) }) }) context('fixture', () => { beforeEach(function () { Fixtures.scaffold('todos') return this.setup({ projectRoot: Fixtures.projectPath('todos'), config: { integrationFolder: 'tests', fixturesFolder: 'tests/_fixtures', }, }) }) it('returns fixture contents', function () { return this.rp({ url: 'http://localhost:2020/__cypress/xhrs/bar', json: true, headers: { 'x-cypress-response': 'fixture:foo', }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.headers['content-type']).to.match(/application\/json/) expect(res.body).to.deep.eq([{ json: true }]) }) }) it('returns __error on fixture errors', function () { return this.rp({ url: 'http://localhost:2020/__cypress/xhrs/bar', json: true, headers: { 'x-cypress-response': 'fixture:bad_json', }, }) .then((res) => { expect(res.statusCode).to.eq(400) expect(res.headers['content-type']).to.match(/application\/json/) expect(res.body.__error).to.include('\'bad_json\' is not valid JSON.') }) }) it('can change the fixture encoding', function () { return fs.readFileAsync(Fixtures.projectPath('todos/tests/_fixtures/images/flower.png'), 'binary') .then((bin) => { return this.rp({ url: 'http://localhost:2020/__cypress/xhrs/bar', headers: { 'x-cypress-response': 'fixture:images/flower.png,binary', 'x-cypress-headers': JSON.stringify({ 'content-type': 'binary/octet-stream', }), }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.headers['content-type']).to.include('binary/octet-stream') expect(res.headers['content-length']).to.eq(`${bin.length}`) }) }) }) }) context('PUT', () => { it('can issue PUT requests', function () { return this.rp({ method: 'put', url: 'http://localhost:2020/__cypress/xhrs/users/1', json: true, body: { name: 'brian', }, headers: { 'x-cypress-response': JSON.stringify({ id: 123, name: 'brian' }), }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.headers['content-type']).to.match(/application\/json/) expect(res.body).to.deep.eq({ id: 123, name: 'brian' }) }) }) }) context('POST', () => { it('can issue POST requests', function () { return this.rp({ method: 'post', url: 'http://localhost:2020/__cypress/xhrs/users/1', json: true, body: { name: 'brian', }, headers: { 'x-cypress-response': JSON.stringify({ id: 123, name: 'brian' }), }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.headers['content-type']).to.match(/application\/json/) expect(res.body).to.deep.eq({ id: 123, name: 'brian' }) }) }) }) context('HEAD', () => { it('can issue PUT requests', function () { return this.rp({ method: 'head', url: 'http://localhost:2020/__cypress/xhrs/users/1', }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.headers['content-type']).to.match(/text\/plain/) }) }) }) context('DELETE', () => { it('can issue DELETE requests', function () { return this.rp({ method: 'delete', url: 'http://localhost:2020/__cypress/xhrs/users/1', }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.headers['content-type']).to.match(/text\/plain/) }) }) }) }) }) // describe "maximum header size", -> // ## https://github.com/cypress-io/cypress/issues/76 // it "does not bomb on huge headers", -> // json = Fixtures.get("server/really_big_json.json") // supertest(@srv) // .get("/__cypress/xhrs/users") // .set("x-cypress-response", json) // .set("x-cypress-response-2", json) // .expect(200) // .expect("Content-Type", /application\/json/) // .expect(JSON.parse(json)) context('GET /__cypress/iframes/*', () => { beforeEach(() => { Fixtures.scaffold('e2e') Fixtures.scaffold('todos') Fixtures.scaffold('no-server') return Fixtures.scaffold('ids') }) describe('todos', () => { beforeEach(function () { return this.setup({ projectRoot: Fixtures.projectPath('todos'), config: { integrationFolder: 'tests', fixturesFolder: 'tests/_fixtures', supportFile: 'tests/_support/spec_helper.js', javascripts: ['tests/etc/etc.js'], }, }) }) it('renders iframe with variables passed in', function () { const contents = removeWhitespace(Fixtures.get('server/expected_todos_iframe.html')) return this.rp('http://localhost:2020/__cypress/iframes/integration/test2.coffee') .then((res) => { expect(res.statusCode).to.eq(200) const body = cleanResponseBody(res.body) expect(body).to.eq(contents) }) }) it('can send back all tests', function () { const contents = removeWhitespace(Fixtures.get('server/expected_todos_all_tests_iframe.html')) return this.rp('http://localhost:2020/__cypress/iframes/__all') .then((res) => { expect(res.statusCode).to.eq(200) const body = cleanResponseBody(res.body) expect(body).to.eq(contents) }) }) it('can send back tests matching spec filter', function () { // only returns tests with "sub_test" in their names const contents = removeWhitespace(Fixtures.get('server/expected_todos_filtered_tests_iframe.html')) this.project.spec = { specFilter: 'sub_test', } return this.rp('http://localhost:2020/__cypress/iframes/__all') .then((res) => { expect(res.statusCode).to.eq(200) const body = cleanResponseBody(res.body) console.log(body) expect(body).to.eq(contents) }) }) }) describe('no-server', () => { beforeEach(function () { return this.setup({ projectRoot: Fixtures.projectPath('no-server'), config: { integrationFolder: 'my-tests', supportFile: 'helpers/includes.js', fileServerFolder: 'foo', }, }) }) it('renders iframe with variables passed in', function () { const contents = removeWhitespace(Fixtures.get('server/expected_no_server_iframe.html')) return this.rp('http://localhost:2020/__cypress/iframes/integration/test1.js') .then((res) => { expect(res.statusCode).to.eq(200) const body = cleanResponseBody(res.body) expect(body).to.eq(contents) }) }) }) describe('no-server with supportFile: false', () => { beforeEach(function () { return this.setup({ projectRoot: Fixtures.projectPath('no-server'), config: { integrationFolder: 'my-tests', supportFile: false, }, }) }) it('renders iframe without support file', function () { const contents = removeWhitespace(Fixtures.get('server/expected_no_server_no_support_iframe.html')) return this.rp('http://localhost:2020/__cypress/iframes/__all') .then((res) => { expect(res.statusCode).to.eq(200) const body = cleanResponseBody(res.body) expect(body).to.eq(contents) }) }) }) describe('e2e', () => { beforeEach(function () { return this.setup({ projectRoot: Fixtures.projectPath('e2e'), config: { integrationFolder: 'cypress/integration', supportFile: 'cypress/support/commands.js', }, }) }) it('omits support directories', function () { const contents = removeWhitespace(Fixtures.get('server/expected_e2e_iframe.html')) return this.rp('http://localhost:2020/__cypress/iframes/integration/app_spec.coffee') .then((res) => { expect(res.statusCode).to.eq(200) const body = cleanResponseBody(res.body) expect(body).to.eq(contents) }) }) }) describe('ids', () => { beforeEach(function () { return this.setup({ projectRoot: Fixtures.projectPath('ids'), }) }) it('renders iframe with variables passed in', function () { const contents = removeWhitespace(Fixtures.get('server/expected_ids_iframe.html')) return this.rp('http://localhost:2020/__cypress/iframes/integration/foo.coffee') .then((res) => { expect(res.statusCode).to.eq(200) const body = cleanResponseBody(res.body) expect(body).to.eq(contents) }) }) it('can send back all tests', function () { const contents = removeWhitespace(Fixtures.get('server/expected_ids_all_tests_iframe.html')) return this.rp('http://localhost:2020/__cypress/iframes/__all') .then((res) => { expect(res.statusCode).to.eq(200) const body = cleanResponseBody(res.body) expect(body).to.eq(contents) }) }) }) }) context('GET *', () => { context('basic request', () => { beforeEach(function () { return this.setup('http://www.github.com') }) it('basic 200 html response', function () { nock(this.server._remoteOrigin) .get('/') .reply(200, 'hello from bar!', { 'Content-Type': 'text/html', }) return this.rp({ url: 'http://www.github.com/', headers: { 'Cookie': '__cypress.initial=true', }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.include('hello from bar!') }) }) }) context('gzip', () => { beforeEach(function () { return this.setup('http://www.github.com') }) it('unzips, injects, and then rezips initial content', function () { nock(this.server._remoteOrigin) .get('/gzip') .matchHeader('accept-encoding', 'gzip') .replyWithFile(200, Fixtures.path('server/gzip.html.gz'), { 'Content-Type': 'text/html', 'Content-Encoding': 'gzip', }) return this.rp({ url: 'http://www.github.com/gzip', gzip: true, headers: { 'Cookie': '__cypress.initial=true', }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.include('<html>') expect(res.body).to.include('gzip') expect(res.body).to.include('Cypress.') expect(res.body).to.include('document.domain = \'github.com\'') expect(res.body).to.include('</html>') }) }) it('unzips, injects, and then rezips regular http content', function () { nock(this.server._remoteOrigin) .get('/gzip') .matchHeader('accept-encoding', 'gzip') .replyWithFile(200, Fixtures.path('server/gzip.html.gz'), { 'Content-Type': 'text/html', 'Content-Encoding': 'gzip', }) return this.rp({ url: 'http://www.github.com/gzip', gzip: true, headers: { 'Cookie': '__cypress.initial=false', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.include('<html>') expect(res.body).to.include('gzip') expect(res.body).to.include('document.domain = \'github.com\'') expect(res.body).to.include('</html>') }) }) it('does not inject on regular gzip\'d content', function () { nock(this.server._remoteOrigin) .get('/gzip') .matchHeader('accept-encoding', 'gzip') .replyWithFile(200, Fixtures.path('server/gzip.html.gz'), { 'Content-Type': 'text/html', 'Content-Encoding': 'gzip', }) return this.rp({ url: 'http://www.github.com/gzip', gzip: true, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.include('<html>') expect(res.body).to.include('gzip') expect(res.body).not.to.include('document.domain = \'github.com\'') expect(res.body).to.include('</html>') }) }) // https://github.com/cypress-io/cypress/issues/1746 it('can ungzip utf-8 javascript and inject without corrupting it', function () { let js = '' const app = express() app.use(compression({ chunkSize: 64, threshold: 1 })) app.get('/', (req, res) => { res.setHeader('content-type', 'application/javascript; charset=UTF-8') res.setHeader('transfer-encoding', 'chunked') const write = (chunk) => { js += chunk return res.write(chunk) } // note - this is unintentionally invalid JS, just try executing it anywhere write('function ') _.times(100, () => { return write('😡😈'.repeat(10)) }) write(' () { }') return res.end() }) const server = http.createServer(app) return Promise.fromCallback((cb) => { return server.listen(12345, cb) }).then(() => { return this.rp({ url: 'http://localhost:12345', gzip: true, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.deep.eq(js) }) }).finally(() => { return Promise.fromCallback((cb) => { return server.close(cb) }) }) }) }) context('accept-encoding', () => { beforeEach(function () { return this.setup('http://www.github.com') }) it('strips unsupported deflate and br encoding', function () { nock(this.server._remoteOrigin) .get('/accept') .matchHeader('accept-encoding', 'gzip') .reply(200, '<html>accept</html>') return this.rp({ url: 'http://www.github.com/accept', gzip: true, headers: { 'accept-encoding': 'gzip,deflate,br', }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.eq('<html>accept</html>') }) }) it('removes accept-encoding when nothing is supported', function () { nock(this.server._remoteOrigin, { badheaders: ['accept-encoding'], }) .get('/accept') .reply(200, '<html>accept</html>') return this.rp({ url: 'http://www.github.com/accept', gzip: true, headers: { 'accept-encoding': 'foo,bar,baz', }, }) .then((res) => { expect(res.statusCode).to.eq(200) expect(res.body).to.eq('<html>accept</html>') }) }) }) context('304 Not Modified', () => { beforeEach(function () { return this.setup('http://localhost:8080') }) it('sends back a 304', function () { nock(this.server._remoteOrigin) .get('/assets/app.js') .reply(304) return this.rp({ url: 'http://localhost:8080/assets/app.js', headers: { 'Cookie': '__cypress.initial=false', }, }) .then((res) => { expect(res.statusCode).to.eq(304) }) }) }) context('redirects', () => { beforeEach(function () { return this.setup('http://getbootstrap.com') }) it('passes the location header through', function () { nock(this.server._remoteOrigin) .get('/foo') .reply(302, undefined, { 'Location': '/', }) .get('/') .reply(200, '<html></html', { 'Content-Type': 'text/html', }) return this.rp({ url: 'http://getbootstrap.com/foo', headers: { 'Cookie': '__cypress.initial=true', }, }) .then((res) => { expect(res.statusCode).to.eq(302) expect(res.headers['set-cookie']).to.include('__cypress.initial=true; Domain=getbootstrap.com; Path=/') expect(res.headers['location']).to.eq('/') }) }) // this fixes improper url merge where we took query params // and added them needlessly it('doesnt redirect with query params or hashes which werent in location header', function () { nock(this.server._remoteOrigin) .get('/foo?bar=baz') .reply(302, undefined, { 'Location': '/css', }) return this.rp({ url: 'http://getbootstrap.com/foo?bar=baz', headers: { 'Cookie': '__cypress.initial=true', }, }) .then((res) => { expect(res.statusCode).to.eq(302) expect(res.headers['set-cookie']).to.include('__cypress.initial=true; Domain=getbootstrap.com; Path=/') expect(res.headers['location']).to.eq('/css') }) }) it('does redirect with query params if location header includes them', function () { nock(this.server._remoteOrigin) .get('/foo?bar=baz') .reply(302, undefined, { 'Location': '/css?q=search', }) .get('/') .reply(200, '<html></html', { 'Content-Type': 'text/html', }) return this.rp({ url: 'http://getbootstrap.com/foo?bar=baz', headers: { 'Cookie': '__cypress.initial=true', }, }) .then((res) => { expect(res.statusCode).to.eq(302) expect(res.headers['set-cookie']).to.include('__cypress.initial=true; Domain=getbootstrap.com; Path=/') expect(res.headers['location']).to.eq('/css?q=search') }) }) it('does redirect with query params to external domain if location header includes them', function () { nock(this.server._remoteOrigin) .get('/foo?bar=baz') .reply(302, undefined, { 'Location': 'https://www.google.com/search?q=cypress', }) .get('/') .reply(200, '<html></html', { 'Content-Type': 'text/html', }) return this.rp({ url: 'http://getbootstrap.com/foo?bar=baz', headers: { 'Cookie': '__cypress.initial=true', }, }) .then((res) => { expect(res.statusCode).to.eq(302) expect(res.headers['set-cookie']).to.include('__cypress.initial=true; Domain=getbootstrap.com; Path=/') expect(res.headers['location']).to.eq('https://www.google.com/search?q=cypress') }) }) it('sets cookies and removes __cypress.initial when initial is originally false', function () { nock(this.server._remoteOrigin) .get('/css') .reply(302, undefined, { 'Set-Cookie': 'foo=bar; Path=/', 'Location': '/css/', }) return this.rp({ url: 'http://getbootstrap.com/css', headers: { 'Cookie': '__cypress.initial=false', }, }) .then((res) => { expect(res.statusCode).to.eq(302) // since this is not a cypress cookie we do not set the domain expect(res.headers['set-cookie']).to.deep.eq(['foo=bar; Path=/']) expect(res.headers['location']).to.eq('/css/') }) }) return [301, 302, 303, 307, 308].forEach((code) => { it(`handles direct for status code: ${code}`, function () { return this.setup('http://auth.example.com') .then(() => { nock(this.server._remoteOrigin) .get('/login') .reply(code, undefined, { Location: 'http://app.example.com/users/1', }) return this.rp({ url: 'http://auth.example.com/login', headers: { 'Cookie': '__cypress.initial=true', }, }) .then((res) => { expect(res.statusCode).to.eq(code) expect(res.headers['set-cookie']).to.include('__cypress.initial=true; Domain=example.com; Path=/') }) }) }) }) }) context('error handling', () => { beforeEach(function () { return this.setup('http://www.github.com') }) it('passes through status code + content', function () { nock(this.server._remoteOrigin) .get('/index.html') .reply(500, 'server error', { 'Content-Type': 'text/html', }) return this.rp({ url: 'http://www.github.com/index.html', headers: { 'Cookie': '__cypress.initial=true', }, }) .then((res) => { expect(res.statusCode).to.eq(500) expect(res.body).to.include('server error') expect(res.body).to.include('document.domain = \'github.com\'') expect(res.headers['set-cookie']).to.match(/__cypress.initial=;/) }) }) it('sends back socket hang up on request errors which match origin', function () { nock('http://app.github.com') .get('/') .replyWithError('ECONNREFUSED') return this.rp({ url: 'http://app.github.com/', headers: { 'Accept': 'text/html, application/xhtml+xml, */*', }, }) .then(() => { throw new Error('should not reach') }).catch((err) => { expect(err.message).to.eq('Error: socket hang up') }) }) it('sends back socket hang up on actual request errors', function () { return this.setup('http://localhost:64644') .then(() => { return this.rp({ url: 'http://localhost:64644', headers: { 'Accept': 'text/html, application/xhtml+xml, */*', }, }) }).then(() => { throw new Error('should not reach') }).catch((err) => { expect(err.message).to.eq('Error: socket hang up') }) }) it('sends back socket hang up on http errors when no matching origin', function () { return this.rp({ url: 'http://localhost:64644', headers: { 'Accept': 'text/html, application/xhtml+xml, */*', }, }) .then(() => { throw new Error('should not reach') }).catch((err) => { expect(err.message).to.eq('Error: socket hang up') }) }) it('sends back 401 when file server does not receive correct auth', function () { return this.setup('<root>', { config: { fileServerFolder: '/Users/bmann/Dev/projects', }, }) .then(() => { return rp(`http://localhost:${this.server._fileServer.port()}/foo/views/test/index.html`, { resolveWithFullResponse: true, simple: false, }) }).then((res) => { expect(res.statusCode).to.eq(401) }) }) it('sends back 404 when file does not exist locally', function () { return this.setup('<root>', { config: { fileServerFolder: '/Users/bmann/Dev/projects', }, }) .then(() => {