Merge pull request #330 from sharelatex/as-karma-tests

Karma tests
This commit is contained in:
Alasdair Smith 2018-02-22 11:06:56 +00:00 committed by GitHub
commit eae8b5a592
16 changed files with 6282 additions and 248 deletions

View file

@ -0,0 +1,6 @@
FROM node:8.9.4
# Install Google Chrome
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
RUN apt-get update && apt-get install -y google-chrome-stable

View file

@ -63,7 +63,14 @@ pipeline {
}
}
steps {
sh 'make --no-print-directory test_unit test_frontend MOCHA_ARGS="--reporter tap"'
sh 'make --no-print-directory test_unit MOCHA_ARGS="--reporter tap"'
}
}
stage('Frontend Unit Test') {
steps {
// Spawns its own docker containers
sh 'make --no-print-directory test_frontend'
}
}

View file

@ -159,6 +159,7 @@ clean_frontend:
clean_tests:
rm -rf test/unit/js
rm -rf test/unit_frontend/js
rm -rf test/acceptance/js
clean_modules:
@ -181,8 +182,9 @@ test: test_unit test_frontend test_acceptance
test_unit:
npm -q run test:unit -- ${MOCHA_ARGS}
test_frontend:
npm -q run test:frontend -- ${MOCHA_ARGS}
test_frontend: test_clean # stop service
$(MAKE) compile
docker-compose ${DOCKER_COMPOSE_FLAGS} up test_frontend
test_acceptance: test_acceptance_app test_acceptance_modules

View file

@ -1,5 +0,0 @@
#!/bin/bash
set -e;
MOCHA="node_modules/.bin/mocha --recursive --reporter spec"
$MOCHA "$@" test/unit_frontend/js

View file

@ -20,6 +20,15 @@ services:
- mongo
command: node app.js
test_frontend:
build:
context: .
dockerfile: Dockerfile.frontend
volumes:
- .:/app
working_dir: /app
command: npm run test:frontend
redis:
image: redis

View file

@ -0,0 +1,36 @@
module.exports = function (config) {
config.set({
customLaunchers: {
ChromeCustom: {
base: 'ChromeHeadless',
// We must disable the Chrome sandbox when running Chrome inside Docker
// (Chrome's sandbox needs more permissions than Docker allows by
// default)
flags: ['--no-sandbox']
}
},
browsers: ['ChromeCustom'],
files: [
'test/unit_frontend/js/bootstrap.js',
// Angular must be loaded before requirejs to set up angular global
'public/js/libs/angular-1.6.4.min.js',
'public/js/libs/angular-mocks.js',
'public/js/libs/jquery-1.11.1.min.js',
// Set up requirejs
'test/unit_frontend/js/test-main.js',
// Include source & test files, but don't "include" them as requirejs
// handles this for us
{ pattern: 'public/js/**/*.js', included: false },
{ pattern: 'test/unit_frontend/js/**/*.js', included: false }
],
frameworks: ['requirejs', 'mocha', 'chai-sinon'],
plugins: [
require('karma-requirejs'),
require('karma-mocha'),
require('karma-chai-sinon'),
require('karma-chrome-launcher'),
require('karma-tap-reporter')
],
reporters: ['tap']
});
}

File diff suppressed because it is too large Load diff

View file

@ -15,7 +15,7 @@
"test:acceptance:dir": "npm -q run test:acceptance:wait_for_app && npm -q run test:acceptance:run -- $@",
"test:acceptance": "npm -q run test:acceptance:dir -- $@ test/acceptance/js",
"test:unit": "npm -q run compile && bin/unit_test $@",
"test:frontend": "npm -q run compile && bin/frontend_test $@",
"test:frontend": "karma start --single-run",
"compile": "make compile",
"start": "npm -q run compile && node app.js",
"nodemon": "nodemon --config nodemon.json",
@ -119,10 +119,18 @@
"grunt-postcss": "^0.8.0",
"grunt-sed": "^0.1.1",
"grunt-shell": "^2.1.0",
"karma": "^2.0.0",
"karma-chai-sinon": "^0.1.5",
"karma-chrome-launcher": "^2.2.0",
"karma-mocha": "^1.3.0",
"karma-requirejs": "^1.1.0",
"karma-tap-reporter": "0.0.6",
"mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"nodemon": "^1.14.3",
"requirejs": "^2.1.22",
"sandboxed-module": "0.2.0",
"sinon": "^1.17.0",
"sinon-chai": "^2.14.0",
"timekeeper": "",
"translations-sharelatex": "git+https://github.com/sharelatex/translations-sharelatex.git#master",
"webpack": "^3.10.0",

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,4 @@
# Stub out some globals
window.sharelatex = {
sixpackDomain: ''
}

View file

@ -0,0 +1,53 @@
define ['ide/editor/directives/cmEditor'], () ->
describe 'cmEditor', () ->
beforeEach(module('SharelatexApp'))
beforeEach () ->
@richTextInit = sinon.stub()
window.Frontend = {
richText: {
init: @richTextInit
}
}
it 'inits Rich Text', () ->
inject ($compile, $rootScope) ->
$compile('<div cm-editor></div>')($rootScope)
expect(@richTextInit).to.have.been.called
it 'attaches to CM', () ->
inject ($compile, $rootScope) ->
setValue = sinon.stub()
@richTextInit.returns({ setValue: setValue })
getSnapshot = sinon.stub()
detachFromCM = sinon.stub()
attachToCM = sinon.stub()
$rootScope.sharejsDoc = {
getSnapshot: getSnapshot
detachFromCM: detachFromCM
attachToCM: attachToCM
}
$compile('<div cm-editor sharejs-doc="sharejsDoc"></div>')($rootScope)
$rootScope.$digest()
expect(getSnapshot).to.have.been.called
expect(setValue).to.have.been.called
expect(detachFromCM).to.have.been.called
expect(attachToCM).to.have.been.called
it 'detaches from CM when destroyed', () ->
inject ($compile, $rootScope) ->
@richTextInit.returns({ setValue: sinon.stub() })
detachFromCM = sinon.stub()
$rootScope.sharejsDoc = {
getSnapshot: sinon.stub()
detachFromCM: detachFromCM
attachToCM: sinon.stub()
}
$compile('<div cm-editor sharejs-doc="sharejsDoc"></div>')($rootScope)
$rootScope.$digest()
$rootScope.$broadcast('destroy')
expect(detachFromCM).to.have.been.called

View file

@ -1,164 +0,0 @@
Path = require 'path'
SandboxedModule = require "sandboxed-module"
modulePath = Path.join __dirname, '../../../../../public/js/ide/history/HistoryV2Manager'
sinon = require("sinon")
expect = require("chai").expect
describe "HistoryV2Manager", ->
beforeEach ->
@moment = {}
@ColorManager = {}
SandboxedModule.require modulePath, globals:
"define": (dependencies, builder) =>
@HistoryV2Manager = builder(@moment, @ColorManager)
@scope =
$watch: sinon.stub()
$on: sinon.stub()
@ide = {}
@historyManager = new @HistoryV2Manager(@ide, @scope)
it "should setup the history scope on intialization", ->
expect(@scope.history).to.deep.equal({
isV2: true
updates: []
nextBeforeTimestamp: null
atEnd: false
selection: {
updates: []
pathname: null
docs: {}
range: {
fromV: null
toV: null
}
}
diff: null
})
describe "_perDocSummaryOfUpdates", ->
it "should return the range of updates for the docs", ->
result = @historyManager._perDocSummaryOfUpdates([{
pathnames: ["main.tex"]
fromV: 7, toV: 9
},{
pathnames: ["main.tex", "foo.tex"]
fromV: 4, toV: 6
},{
pathnames: ["main.tex"]
fromV: 3, toV: 3
},{
pathnames: ["foo.tex"]
fromV: 0, toV: 2
}])
expect(result).to.deep.equal({
"main.tex": { fromV: 3, toV: 9 },
"foo.tex": { fromV: 0, toV: 6 }
})
it "should track renames", ->
result = @historyManager._perDocSummaryOfUpdates([{
pathnames: ["main2.tex"]
fromV: 5, toV: 9
},{
project_ops: [{
rename: {
pathname: "main1.tex",
newPathname: "main2.tex"
}
}],
fromV: 4, toV: 4
},{
pathnames: ["main1.tex"]
fromV: 3, toV: 3
},{
project_ops: [{
rename: {
pathname: "main0.tex",
newPathname: "main1.tex"
}
}],
fromV: 2, toV: 2
},{
pathnames: ["main0.tex"]
fromV: 0, toV: 1
}])
expect(result).to.deep.equal({
"main0.tex": { fromV: 0, toV: 9 }
})
it "should track single renames", ->
result = @historyManager._perDocSummaryOfUpdates([{
project_ops: [{
rename: {
pathname: "main1.tex",
newPathname: "main2.tex"
}
}],
fromV: 4, toV: 5
}])
expect(result).to.deep.equal({
"main1.tex": { fromV: 4, toV: 5 }
})
it "should track additions", ->
result = @historyManager._perDocSummaryOfUpdates([{
project_ops: [{
add:
pathname: "main.tex"
}]
fromV: 0, toV: 1
}, {
pathnames: ["main.tex"]
fromV: 1, toV: 4
}])
expect(result).to.deep.equal({
"main.tex": { fromV: 0, toV: 4 }
})
it "should track single additions", ->
result = @historyManager._perDocSummaryOfUpdates([{
project_ops: [{
add:
pathname: "main.tex"
}]
fromV: 0, toV: 1
}])
expect(result).to.deep.equal({
"main.tex": { fromV: 0, toV: 1 }
})
it "should track deletions", ->
result = @historyManager._perDocSummaryOfUpdates([{
pathnames: ["main.tex"]
fromV: 0, toV: 1
}, {
project_ops: [{
remove:
pathname: "main.tex"
}]
fromV: 1, toV: 2
}])
expect(result).to.deep.equal({
"main.tex": { fromV: 0, toV: 2, deleted: true }
})
it "should track single deletions", ->
result = @historyManager._perDocSummaryOfUpdates([{
project_ops: [{
remove:
pathname: "main.tex"
}]
fromV: 0, toV: 1
}])
expect(result).to.deep.equal({
"main.tex": { fromV: 0, toV: 1, deleted: true }
})

View file

@ -0,0 +1,152 @@
define ['ide/history/HistoryV2Manager'], (HistoryV2Manager) ->
describe "HistoryV2Manager", ->
beforeEach ->
@scope =
$watch: sinon.stub()
$on: sinon.stub()
@ide = {}
@historyManager = new HistoryV2Manager(@ide, @scope)
it "should setup the history scope on intialization", ->
expect(@scope.history).to.deep.equal({
isV2: true
updates: []
nextBeforeTimestamp: null
atEnd: false
selection: {
updates: []
pathname: null
docs: {}
range: {
fromV: null
toV: null
}
}
diff: null
})
describe "_perDocSummaryOfUpdates", ->
it "should return the range of updates for the docs", ->
result = @historyManager._perDocSummaryOfUpdates([{
pathnames: ["main.tex"]
fromV: 7, toV: 9
},{
pathnames: ["main.tex", "foo.tex"]
fromV: 4, toV: 6
},{
pathnames: ["main.tex"]
fromV: 3, toV: 3
},{
pathnames: ["foo.tex"]
fromV: 0, toV: 2
}])
expect(result).to.deep.equal({
"main.tex": { fromV: 3, toV: 9 },
"foo.tex": { fromV: 0, toV: 6 }
})
it "should track renames", ->
result = @historyManager._perDocSummaryOfUpdates([{
pathnames: ["main2.tex"]
fromV: 5, toV: 9
},{
project_ops: [{
rename: {
pathname: "main1.tex",
newPathname: "main2.tex"
}
}],
fromV: 4, toV: 4
},{
pathnames: ["main1.tex"]
fromV: 3, toV: 3
},{
project_ops: [{
rename: {
pathname: "main0.tex",
newPathname: "main1.tex"
}
}],
fromV: 2, toV: 2
},{
pathnames: ["main0.tex"]
fromV: 0, toV: 1
}])
expect(result).to.deep.equal({
"main0.tex": { fromV: 0, toV: 9 }
})
it "should track single renames", ->
result = @historyManager._perDocSummaryOfUpdates([{
project_ops: [{
rename: {
pathname: "main1.tex",
newPathname: "main2.tex"
}
}],
fromV: 4, toV: 5
}])
expect(result).to.deep.equal({
"main1.tex": { fromV: 4, toV: 5 }
})
it "should track additions", ->
result = @historyManager._perDocSummaryOfUpdates([{
project_ops: [{
add:
pathname: "main.tex"
}]
fromV: 0, toV: 1
}, {
pathnames: ["main.tex"]
fromV: 1, toV: 4
}])
expect(result).to.deep.equal({
"main.tex": { fromV: 0, toV: 4 }
})
it "should track single additions", ->
result = @historyManager._perDocSummaryOfUpdates([{
project_ops: [{
add:
pathname: "main.tex"
}]
fromV: 0, toV: 1
}])
expect(result).to.deep.equal({
"main.tex": { fromV: 0, toV: 1 }
})
it "should track deletions", ->
result = @historyManager._perDocSummaryOfUpdates([{
pathnames: ["main.tex"]
fromV: 0, toV: 1
}, {
project_ops: [{
remove:
pathname: "main.tex"
}]
fromV: 1, toV: 2
}])
expect(result).to.deep.equal({
"main.tex": { fromV: 0, toV: 2, deleted: true }
})
it "should track single deletions", ->
result = @historyManager._perDocSummaryOfUpdates([{
project_ops: [{
remove:
pathname: "main.tex"
}]
fromV: 0, toV: 1
}])
expect(result).to.deep.equal({
"main.tex": { fromV: 0, toV: 1, deleted: true }
})

View file

@ -1,68 +0,0 @@
Path = require 'path'
SandboxedModule = require "sandboxed-module"
modulePath = Path.join __dirname, '../../../../../public/js/ide/history/util/displayNameForUser'
sinon = require("sinon")
expect = require("chai").expect
describe "displayNameForUser", ->
beforeEach ->
SandboxedModule.require modulePath, globals:
"define": (dependencies, builder) =>
@displayNameForUser = builder()
"window": @window = {}
@window.user = { id: 42 }
it "should return 'Anonymous' with no user", ->
expect(
@displayNameForUser(null)
).to.equal "Anonymous"
it "should return 'you' when the user has the same id as the window", ->
expect(
@displayNameForUser({
id: @window.user.id
email: "james.allen@overleaf.com"
first_name: "James"
last_name: "Allen"
})
).to.equal "you"
it "should return the first_name and last_name when present", ->
expect(
@displayNameForUser({
id: @window.user.id + 1
email: "james.allen@overleaf.com"
first_name: "James"
last_name: "Allen"
})
).to.equal "James Allen"
it "should return only the firstAname if no last_name", ->
expect(
@displayNameForUser({
id: @window.user.id + 1
email: "james.allen@overleaf.com"
first_name: "James"
last_name: ""
})
).to.equal "James"
it "should return the email username if there are no names", ->
expect(
@displayNameForUser({
id: @window.user.id + 1
email: "james.allen@overleaf.com"
first_name: ""
last_name: ""
})
).to.equal "james.allen"
it "should return the '?' if it has nothing", ->
expect(
@displayNameForUser({
id: @window.user.id + 1
email: ""
first_name: ""
last_name: ""
})
).to.equal "?"

View file

@ -0,0 +1,59 @@
define ['ide/history/util/displayNameForUser'], (displayNameForUser) ->
describe "displayNameForUser", ->
beforeEach ->
window.user = { id: 42 }
it "should return 'Anonymous' with no user", ->
expect(
displayNameForUser(null)
).to.equal "Anonymous"
it "should return 'you' when the user has the same id as the window", ->
expect(
displayNameForUser({
id: window.user.id
email: "james.allen@overleaf.com"
first_name: "James"
last_name: "Allen"
})
).to.equal "you"
it "should return the first_name and last_name when present", ->
expect(
displayNameForUser({
id: window.user.id + 1
email: "james.allen@overleaf.com"
first_name: "James"
last_name: "Allen"
})
).to.equal "James Allen"
it "should return only the firstAname if no last_name", ->
expect(
displayNameForUser({
id: window.user.id + 1
email: "james.allen@overleaf.com"
first_name: "James"
last_name: ""
})
).to.equal "James"
it "should return the email username if there are no names", ->
expect(
displayNameForUser({
id: window.user.id + 1
email: "james.allen@overleaf.com"
first_name: ""
last_name: ""
})
).to.equal "james.allen"
it "should return the '?' if it has nothing", ->
expect(
displayNameForUser({
id: window.user.id + 1
email: ""
first_name: ""
last_name: ""
})
).to.equal "?"

View file

@ -0,0 +1,14 @@
# Set up requirejs to load the tests
# Uses heuristic that test filenames end with Tests.js
tests = []
for file of window.__karma__.files
if window.__karma__.files.hasOwnProperty(file)
if /Tests\.js$/.test(file)
tests.push(file)
requirejs.config
baseUrl: '/base/public/js'
paths:
"moment": "libs/moment-2.9.0"
deps: tests
callback: window.__karma__.start