UNPKG

@revoloo/cypress6

Version:

Cypress.io end to end testing tool

483 lines (390 loc) 13 kB
require('../spec_helper') const _ = require('lodash') const os = require('os') const express = require('express') const Promise = require('bluebird') const { connect } = require('@packages/network') const config = require(`${root}lib/config`) const logger = require(`${root}lib/logger`) const { ServerE2E } = require(`${root}lib/server-e2e`) const { SocketE2E } = require(`${root}lib/socket-e2e`) const fileServer = require(`${root}lib/file_server`) const ensureUrl = require(`${root}lib/util/ensure-url`) const morganFn = function () {} mockery.registerMock('morgan', () => { return morganFn }) xdescribe('lib/server', () => { beforeEach(function () { this.fileServer = { close () {}, port () { return 1111 }, } sinon.stub(fileServer, 'create').returns(this.fileServer) return config.set({ projectRoot: '/foo/bar/' }) .then((cfg) => { this.config = cfg this.server = new ServerE2E() this.oldFileServer = this.server._fileServer this.server._fileServer = this.fileServer }) }) afterEach(function () { return this.server && this.server.close() }) context('#createExpressApp', () => { beforeEach(function () { this.use = sinon.spy(express.application, 'use') }) it('instantiates express instance without morgan', function () { const app = this.server.createExpressApp({ morgan: false }) expect(app.get('view engine')).to.eq('html') expect(this.use).not.to.be.calledWith(morganFn) }) it('requires morgan if true', function () { this.server.createExpressApp({ morgan: true }) expect(this.use).to.be.calledWith(morganFn) }) }) context('#open', () => { beforeEach(function () { return sinon.stub(this.server, 'createServer').resolves() }) it('calls #createExpressApp with morgan', function () { sinon.spy(this.server, 'createExpressApp') _.extend(this.config, { port: 54321, morgan: false }) return this.server.open(this.config) .then(() => { expect(this.server.createExpressApp).to.be.calledWithMatch({ morgan: false }) }) }) it('calls #createServer with port', function () { _.extend(this.config, { port: 54321 }) const obj = {} sinon.stub(this.server, 'createRoutes') sinon.stub(this.server, 'createExpressApp').returns(obj) return this.server.open(this.config) .then(() => { expect(this.server.createServer).to.be.calledWith(obj, this.config) }) }) it('calls #createRoutes with app + config', function () { const app = {} const project = {} const onError = sinon.spy() sinon.stub(this.server, 'createRoutes') sinon.stub(this.server, 'createExpressApp').returns(app) return this.server.open(this.config, project, onError) .then(() => { expect(this.server.createRoutes).to.be.called expect(this.server.createRoutes.lastCall.args[0].app).to.equal(app) expect(this.server.createRoutes.lastCall.args[0].config).to.equal(this.config) expect(this.server.createRoutes.lastCall.args[0].project).to.equal(project) expect(this.server.createRoutes.lastCall.args[0].onError).to.equal(onError) }) }) it('calls #createServer with port + fileServerFolder + socketIoRoute + app', function () { const obj = {} sinon.stub(this.server, 'createRoutes') sinon.stub(this.server, 'createExpressApp').returns(obj) return this.server.open(this.config) .then(() => { expect(this.server.createServer).to.be.calledWith(obj, this.config) }) }) it('calls logger.setSettings with config', function () { sinon.spy(logger, 'setSettings') return this.server.open(this.config) .then((ret) => { expect(logger.setSettings).to.be.calledWith(this.config) }) }) }) context('#createServer', () => { beforeEach(function () { this.port = 54321 this.app = this.server.createExpressApp({ morgan: true }) }) it('isListening=true', function () { return this.server.createServer(this.app, { port: this.port }) .then(() => { expect(this.server.isListening).to.be.true }) }) it('resolves with http server port', function () { return this.server.createServer(this.app, { port: this.port }) .spread((port) => { expect(port).to.eq(this.port) }) }) it('all servers listen only on localhost and no other interface', function () { fileServer.create.restore() this.server._fileServer = this.oldFileServer const interfaces = _.flatten(_.values(os.networkInterfaces())) const nonLoopback = interfaces.find((iface) => { return (iface.family === 'IPv4') && (iface.address !== '127.0.0.1') }) // verify that we can connect to `port` over loopback // and not over another configured IPv4 address const tryOnlyLoopbackConnect = (port) => { return Promise.all([ connect.byPortAndAddress(port, '127.0.0.1'), connect.byPortAndAddress(port, nonLoopback) .then(() => { throw new Error(`Shouldn't be able to connect on ${nonLoopback.address}:${port}`) }).catch({ errno: 'ECONNREFUSED' }, () => {}), ]) } return this.server.createServer(this.app, {}) .spread((port) => { return Promise.map( [ port, this.server._fileServer.port(), this.server._httpsProxy._sniPort, ], tryOnlyLoopbackConnect, ) }) }) it('resolves with warning if cannot connect to baseUrl', function () { sinon.stub(ensureUrl, 'isListening').rejects() return this.server.createServer(this.app, { port: this.port, baseUrl: `http://localhost:${this.port}` }) .spread((port, warning) => { expect(warning.type).to.eq('CANNOT_CONNECT_BASE_URL_WARNING') expect(warning.message).to.include(this.port) }) }) context('errors', () => { it('rejects with portInUse', function () { return this.server.createServer(this.app, { port: this.port }) .then(() => { return this.server.createServer(this.app, { port: this.port }) }).then(() => { throw new Error('should have failed but didn\'t') }).catch((err) => { expect(err.type).to.eq('PORT_IN_USE_SHORT') expect(err.message).to.include(this.port) }) }) }) }) context('#end', () => { it('calls this._socket.end', function () { const socket = sinon.stub({ end () {}, close () {}, }) this.server._socket = socket this.server.end() expect(socket.end).to.be.called }) it('is noop without this._socket', function () { return this.server.end() }) }) context('#startWebsockets', () => { beforeEach(function () { this.startListening = sinon.stub(SocketE2E.prototype, 'startListening') }) it('sets _socket and calls _socket#startListening', function () { return this.server.open(this.config) .then(() => { const arg2 = {} this.server.startWebsockets(1, 2, arg2) expect(this.startListening).to.be.calledWith(this.server.getHttpServer(), 1, 2, arg2) }) }) }) context('#reset', () => { beforeEach(function () { return this.server.open(this.config) .then(() => { this.buffers = this.server._networkProxy.http return sinon.stub(this.buffers, 'reset') }) }) it('resets the buffers', function () { this.server.reset() expect(this.buffers.reset).to.be.called }) it('sets the domain to the previous base url if set', function () { this.server._baseUrl = 'http://localhost:3000' this.server.reset() expect(this.server._remoteStrategy).to.equal('http') }) it('sets the domain to <root> if not set', function () { this.server.reset() expect(this.server._remoteStrategy).to.equal('file') }) }) context('#close', () => { it('returns a promise', function () { expect(this.server.close()).to.be.instanceof(Promise) }) it('calls close on this.server', function () { return this.server.open(this.config) .then(() => { return this.server.close() }) }) it('isListening=false', function () { return this.server.open(this.config) .then(() => { return this.server.close() }).then(() => { expect(this.server.isListening).to.be.false }) }) it('clears settings from Log', function () { logger.setSettings({}) return this.server.close() .then(() => { expect(logger.getSettings()).to.be.undefined }) }) it('calls close on this._socket', function () { this.server._socket = { close: sinon.spy() } return this.server.close() .then(() => { expect(this.server._socket.close).to.be.calledOnce }) }) }) context('#proxyWebsockets', () => { beforeEach(function () { this.proxy = sinon.stub({ ws () {}, on () {}, }) this.socket = sinon.stub({ end () {} }) this.head = {} }) it('is noop if req.url startsWith socketIoRoute', function () { const socket = { remotePort: 12345, remoteAddress: '127.0.0.1', } this.server._socketAllowed.add({ localPort: socket.remotePort, once: _.noop, }) const noop = this.server.proxyWebsockets(this.proxy, '/foo', { url: '/foobarbaz', socket, }) expect(noop).to.be.undefined }) it('calls proxy.ws with hostname + port', function () { this.server._onDomainSet('https://www.google.com') const req = { connection: { encrypted: true, }, url: '/', headers: { host: 'www.google.com', }, } this.server.proxyWebsockets(this.proxy, '/foo', req, this.socket, this.head) expect(this.proxy.ws).to.be.calledWithMatch(req, this.socket, this.head, { secure: false, target: { host: 'www.google.com', port: '443', protocol: 'https:', }, }) }) it('ends the socket if its writable and there is no __cypress.remoteHost', function () { const req = { url: '/', headers: { cookie: 'foo=bar', }, } this.server.proxyWebsockets(this.proxy, '/foo', req, this.socket, this.head) expect(this.socket.end).not.to.be.called this.socket.writable = true this.server.proxyWebsockets(this.proxy, '/foo', req, this.socket, this.head) expect(this.socket.end).to.be.called }) }) context('#_onDomainSet', () => { beforeEach(function () { this.server = new ServerE2E() }) it('sets port to 443 when omitted and https:', function () { const ret = this.server._onDomainSet('https://staging.google.com/foo/bar') expect(ret).to.deep.eq({ auth: undefined, origin: 'https://staging.google.com', strategy: 'http', domainName: 'google.com', visiting: undefined, fileServer: null, props: { port: '443', domain: 'google', tld: 'com', }, }) }) it('sets port to 80 when omitted and http:', function () { const ret = this.server._onDomainSet('http://staging.google.com/foo/bar') expect(ret).to.deep.eq({ auth: undefined, origin: 'http://staging.google.com', strategy: 'http', domainName: 'google.com', visiting: undefined, fileServer: null, props: { port: '80', domain: 'google', tld: 'com', }, }) }) it('sets host + port to localhost', function () { const ret = this.server._onDomainSet('http://localhost:4200/a/b?q=1#asdf') expect(ret).to.deep.eq({ auth: undefined, origin: 'http://localhost:4200', strategy: 'http', domainName: 'localhost', visiting: undefined, fileServer: null, props: { port: '4200', domain: '', tld: 'localhost', }, }) }) it('sets <root> when not http url', function () { this.server._server = { address () { return { port: 9999 } }, } this.server._fileServer = { port () { return 9998 }, } const ret = this.server._onDomainSet('/index.html') expect(ret).to.deep.eq({ auth: undefined, origin: 'http://localhost:9999', strategy: 'file', domainName: 'localhost', fileServer: 'http://localhost:9998', props: null, visiting: undefined, }) }) }) })