mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05:00
Merge pull request #15006 from overleaf/mj-bad-karma
[web] Remove karma tests and config GitOrigin-RevId: a157f46d1ecf93fcbc99713a1c3aa11f82e5a619
This commit is contained in:
parent
3e34467e12
commit
6b3dac803d
13 changed files with 7 additions and 2181 deletions
998
package-lock.json
generated
998
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -1,13 +0,0 @@
|
|||
diff --git a/node_modules/finalhandler/index.js b/node_modules/finalhandler/index.js
|
||||
index 5673507..40f4684 100644
|
||||
--- a/node_modules/finalhandler/index.js
|
||||
+++ b/node_modules/finalhandler/index.js
|
||||
@@ -125,7 +125,7 @@ function finalhandler (req, res, options) {
|
||||
// cannot actually respond
|
||||
if (headersSent(res)) {
|
||||
debug('cannot %d after headers sent', status)
|
||||
- req.socket.destroy()
|
||||
+ if (req.socket) req.socket.destroy()
|
||||
return
|
||||
}
|
||||
|
|
@ -37,7 +37,6 @@
|
|||
"**/app/src/**/*.js",
|
||||
"app.js",
|
||||
"i18next-scanner.config.js",
|
||||
"karma.conf.js",
|
||||
"scripts/**/*.js",
|
||||
"webpack.config*.js"
|
||||
],
|
||||
|
@ -82,14 +81,6 @@
|
|||
"mocha/prefer-arrow-callback": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
// Frontend test specific rules
|
||||
"files": ["**/test/karma/**/*.js"],
|
||||
"globals": {
|
||||
"expect": true,
|
||||
"$": true
|
||||
}
|
||||
},
|
||||
{
|
||||
// Backend specific rules
|
||||
"files": ["**/app/src/**/*.js", "app.js"],
|
||||
|
|
|
@ -45,8 +45,6 @@ clean:
|
|||
-COMPOSE_PROJECT_NAME=acceptance_modules_merged_saas_4_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) down --rmi local
|
||||
-COMPOSE_PROJECT_NAME=acceptance_modules_merged_server_ce_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) down --rmi local
|
||||
-COMPOSE_PROJECT_NAME=acceptance_modules_merged_server_pro_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) down --rmi local
|
||||
-COMPOSE_PROJECT_NAME=karma_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) down --rmi local
|
||||
-COMPOSE_PROJECT_NAME=karma_test_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) down --rmi local
|
||||
-COMPOSE_PROJECT_NAME=frontend_test_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) down --rmi local
|
||||
-COMPOSE_PROJECT_NAME=tar_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) down --rmi local
|
||||
|
||||
|
@ -60,7 +58,7 @@ clean_ci:
|
|||
# Tests
|
||||
#
|
||||
|
||||
test: test_unit test_karma test_acceptance test_frontend test_frontend_ct
|
||||
test: test_unit test_acceptance test_frontend test_frontend_ct
|
||||
|
||||
test_module: test_unit_module test_acceptance_module
|
||||
|
||||
|
@ -115,18 +113,6 @@ test_unit_modules: $(TEST_UNIT_MODULES)
|
|||
test_unit_module:
|
||||
$(MAKE) modules/$(MODULE_NAME)/test_unit
|
||||
|
||||
#
|
||||
# Karma frontend tests
|
||||
#
|
||||
|
||||
test_karma: build_test_karma test_karma_run
|
||||
|
||||
test_karma_run:
|
||||
COMPOSE_PROJECT_NAME=karma_test_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) down -v -t 0
|
||||
COMPOSE_PROJECT_NAME=karma_test_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) run --rm test_karma
|
||||
COMPOSE_PROJECT_NAME=karma_test_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) down -v -t 0
|
||||
|
||||
test_karma_build_run: build_test_karma test_karma_run
|
||||
|
||||
#
|
||||
# Frontend tests
|
||||
|
@ -445,7 +431,7 @@ lint_test_modules:
|
|||
--max-warnings=0
|
||||
|
||||
lint: lint_misc
|
||||
# migrations, scripts, webpack config, karma config
|
||||
# migrations, scripts, webpack config
|
||||
lint_misc:
|
||||
npx eslint . \
|
||||
--ignore-pattern 'app.{js,jsx,mjs,ts,tsx}' \
|
||||
|
@ -558,9 +544,6 @@ build:
|
|||
--file Dockerfile \
|
||||
../..
|
||||
|
||||
build_test_karma:
|
||||
COMPOSE_PROJECT_NAME=karma_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) build test_karma
|
||||
|
||||
publish:
|
||||
docker push $(DOCKER_REPO)/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER)
|
||||
|
||||
|
@ -583,7 +566,7 @@ $(MODULE_TARGETS):
|
|||
$(MODULE_TARGETS) \
|
||||
compile_modules compile_modules_full clean_ci \
|
||||
test test_module test_unit test_unit_app \
|
||||
test_unit_modules test_unit_module test_karma test_karma_run \
|
||||
test_karma_build_run test_frontend test_acceptance test_acceptance_app \
|
||||
test_acceptance_modules test_acceptance_module ci format format_fix lint \
|
||||
build build_test_karma publish tar
|
||||
test_unit_modules test_unit_module test_frontend \
|
||||
test_acceptance test_acceptance_app test_acceptance_modules \
|
||||
test_acceptance_module ci format format_fix lint \
|
||||
build publish tar
|
||||
|
|
|
@ -38,20 +38,6 @@ services:
|
|||
- saml
|
||||
- ldap
|
||||
|
||||
test_karma:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: services/web/Dockerfile.frontend.ci
|
||||
args:
|
||||
PROJECT_NAME: $PROJECT_NAME
|
||||
BRANCH_NAME: $BRANCH_NAME
|
||||
BUILD_NUMBER: $BUILD_NUMBER
|
||||
working_dir: /overleaf/services/web
|
||||
command: npm run test:karma:single
|
||||
user: node
|
||||
environment:
|
||||
NODE_OPTIONS: "--unhandled-rejections=strict"
|
||||
|
||||
test_frontend:
|
||||
build:
|
||||
context: ../..
|
||||
|
|
|
@ -46,19 +46,6 @@ services:
|
|||
- ldap
|
||||
command: npm run --silent test:acceptance:app
|
||||
|
||||
test_karma:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: services/web/Dockerfile.frontend
|
||||
volumes:
|
||||
- .:/overleaf/services/web
|
||||
- ../../node_modules:/overleaf/node_modules
|
||||
- ../../libraries:/overleaf/libraries
|
||||
environment:
|
||||
NODE_OPTIONS: "--unhandled-rejections=strict"
|
||||
working_dir: /overleaf/services/web
|
||||
command: npm run --silent test:karma:single
|
||||
|
||||
test_frontend:
|
||||
build:
|
||||
context: ../..
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
const webpackConfig = require('./webpack.config.test')
|
||||
|
||||
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: [
|
||||
// Import all tests (see comment in the file for why this is necessary)
|
||||
'test/karma/import_tests.js',
|
||||
],
|
||||
middleware: ['fake-img'],
|
||||
preprocessors: {
|
||||
// Run files through webpack
|
||||
'test/karma/import_tests.js': ['webpack'],
|
||||
},
|
||||
frameworks: ['mocha', 'chai-sinon'],
|
||||
// Configure webpack in the tests
|
||||
webpack: webpackConfig,
|
||||
// Configure the webpack dev server used to serve test files
|
||||
webpackMiddleware: {
|
||||
// Disable file-watching -- it is of no use in CI, we use single runs.
|
||||
// https://webpack.js.org/configuration/watch/
|
||||
watch: false,
|
||||
// ^ does not work when placed in webpack.config.test.
|
||||
// webpack-dev-middleware overrides it :/
|
||||
// v seems to be supported, according to
|
||||
// https://www.npmjs.com/package/webpack-dev-middleware#watchoptions
|
||||
watchOptions: {
|
||||
ignored: [/node_modules/, /frontend/, /test/],
|
||||
},
|
||||
|
||||
// Disable noisy CLI output
|
||||
stats: 'errors-only',
|
||||
},
|
||||
plugins: [
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-mocha'),
|
||||
require('karma-chai-sinon'),
|
||||
require('karma-webpack'),
|
||||
require('karma-mocha-reporter'),
|
||||
{ 'middleware:fake-img': ['factory', fakeImgMiddlewareFactory] },
|
||||
],
|
||||
reporters: ['mocha'],
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle fake images
|
||||
*/
|
||||
function fakeImgMiddlewareFactory() {
|
||||
return function (req, res, next) {
|
||||
if (req.originalUrl.startsWith('/fake/')) {
|
||||
return res.end('fake img response')
|
||||
}
|
||||
next()
|
||||
}
|
||||
}
|
|
@ -15,8 +15,6 @@
|
|||
"test:unit:app": "npm run test:unit:run_dir -- test/unit/src",
|
||||
"test:frontend": "NODE_ENV=test TZ=GMT mocha --recursive --timeout 5000 --exit --extension js,jsx,mjs,ts,tsx --grep=$MOCHA_GREP --require test/frontend/bootstrap.js --ignore '**/*.spec.{js,jsx,ts,tsx}' test/frontend modules/*/test/frontend",
|
||||
"test:frontend:coverage": "c8 --all --include 'frontend/js' --include 'modules/*/frontend/js' --exclude 'frontend/js/vendor' --reporter=lcov --reporter=text-summary npm run test:frontend",
|
||||
"test:karma": "karma start",
|
||||
"test:karma:single": "karma start --no-auto-watch --single-run",
|
||||
"start": "node $NODE_APP_OPTIONS app.js",
|
||||
"nodemon": "node --watch $NODE_APP_OPTIONS app.js --watch-locales",
|
||||
"webpack": "webpack serve --config webpack.config.dev.js",
|
||||
|
@ -58,7 +56,7 @@
|
|||
"local:test:acceptance": "npm run local:test:acceptance:app && npm run local:test:acceptance:modules",
|
||||
"local:test:unit": "npm run test:unit:all",
|
||||
"local:test:frontend": "npm run test:frontend",
|
||||
"local:test": "npm run local:test:unit && npm run local:test:frontend && npm run test:karma:single && npm run local:test:acceptance"
|
||||
"local:test": "npm run local:test:unit && npm run local:test:frontend && npm run local:test:acceptance"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 1 year",
|
||||
|
@ -289,13 +287,6 @@
|
|||
"jquery": "^2.2.4",
|
||||
"jsdom": "^19.0.0",
|
||||
"jsdom-global": "^3.0.2",
|
||||
"karma": "^6.3.17",
|
||||
"karma-chai-sinon": "^0.1.5",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-mocha": "^2.0.0",
|
||||
"karma-mocha-reporter": "^2.2.5",
|
||||
"karma-requirejs": "^1.1.0",
|
||||
"karma-webpack": "^5.0.0",
|
||||
"less": "^3.11.1",
|
||||
"less-loader": "^11.1.3",
|
||||
"match-sorter": "^6.2.0",
|
||||
|
|
|
@ -1,308 +0,0 @@
|
|||
/* eslint-disable
|
||||
max-len,
|
||||
*/
|
||||
/* global inject, sinon */
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
import SpellCheckManager from '../../../../../../frontend/js/ide/editor/directives/aceEditor/spell-check/SpellCheckManager'
|
||||
|
||||
export default describe('SpellCheckManager', function () {
|
||||
beforeEach(function (done) {
|
||||
this.timelord = sinon.useFakeTimers()
|
||||
window.user = { id: 1 }
|
||||
window.csrfToken = 'token'
|
||||
this.scope = {
|
||||
$watch: sinon.stub(),
|
||||
spellCheck: true,
|
||||
spellCheckLanguage: 'en',
|
||||
}
|
||||
this.highlightedWordManager = {
|
||||
reset: sinon.stub(),
|
||||
clearRow: sinon.stub(),
|
||||
addHighlight: sinon.stub(),
|
||||
}
|
||||
this.adapter = {
|
||||
getLineCount: sinon.stub(),
|
||||
getFirstVisibleRowNum: sinon.stub(),
|
||||
getLastVisibleRowNum: sinon.stub(),
|
||||
getLinesByRows: sinon.stub(),
|
||||
highlightedWordManager: this.highlightedWordManager,
|
||||
}
|
||||
return inject(($q, $http, $httpBackend, $cacheFactory) => {
|
||||
this.$http = $http
|
||||
this.$q = $q
|
||||
this.$httpBackend = $httpBackend
|
||||
this.spellCheckManager = new SpellCheckManager(
|
||||
this.scope,
|
||||
$cacheFactory,
|
||||
$http,
|
||||
$q,
|
||||
this.adapter
|
||||
)
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
return this.timelord.restore()
|
||||
})
|
||||
|
||||
it('adds an highlight when a misspelling is found', function () {
|
||||
this.$httpBackend.when('POST', '/spelling/check').respond({
|
||||
misspellings: [
|
||||
{
|
||||
index: 0,
|
||||
suggestions: ['opposition'],
|
||||
},
|
||||
],
|
||||
})
|
||||
this.adapter.getLinesByRows.returns(['oppozition'])
|
||||
this.spellCheckManager.init()
|
||||
this.timelord.tick(500)
|
||||
this.$httpBackend.flush()
|
||||
expect(this.highlightedWordManager.addHighlight).to.have.been.called
|
||||
})
|
||||
|
||||
describe('runSpellCheck', function () {
|
||||
beforeEach(function () {
|
||||
this.adapter.getLineCount.returns(10)
|
||||
this.adapter.getFirstVisibleRowNum.returns(3)
|
||||
this.adapter.getLastVisibleRowNum.returns(5)
|
||||
this.adapter.getLinesByRows.returns([
|
||||
'Lorem ipsum dolor sit amet',
|
||||
'consectetur adipisicing elit',
|
||||
'sed do eiusmod',
|
||||
])
|
||||
this.$httpBackend.when('POST', '/spelling/check').respond({
|
||||
misspellings: [
|
||||
{
|
||||
index: 0,
|
||||
suggestions: ['opposition'],
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
describe('when doing the first check', function () {
|
||||
beforeEach(function () {
|
||||
this.spellCheckManager.init()
|
||||
})
|
||||
it('initially flags all lines as dirty ', function () {
|
||||
expect(this.spellCheckManager.changedLines)
|
||||
.to.have.lengthOf(10)
|
||||
.and.to.not.include(false)
|
||||
})
|
||||
it('checks beyond the currently visible viewport', function () {
|
||||
this.timelord.tick(500)
|
||||
this.$httpBackend.flush()
|
||||
expect(this.adapter.getLinesByRows).to.have.been.calledWith([
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
||||
])
|
||||
})
|
||||
})
|
||||
describe('after the initial check', function () {
|
||||
beforeEach(function () {
|
||||
this.spellCheckManager.init()
|
||||
this.spellCheckManager.firstCheck = false
|
||||
})
|
||||
|
||||
it('only checks visible lines', function () {
|
||||
this.spellCheckManager.runSpellCheck()
|
||||
this.spellCheckManager.timeoutId = null
|
||||
this.$httpBackend.flush()
|
||||
expect(this.adapter.getLinesByRows).to.have.been.calledWith([3, 4, 5])
|
||||
})
|
||||
|
||||
it('flags checked lines as non-dirty', function () {
|
||||
this.spellCheckManager.runSpellCheck()
|
||||
this.spellCheckManager.timeoutId = null
|
||||
this.$httpBackend.flush()
|
||||
expect(this.spellCheckManager.changedLines[2]).to.equal(true)
|
||||
expect(this.spellCheckManager.changedLines[3]).to.equal(false)
|
||||
expect(this.spellCheckManager.changedLines[4]).to.equal(false)
|
||||
expect(this.spellCheckManager.changedLines[5]).to.equal(false)
|
||||
expect(this.spellCheckManager.changedLines[6]).to.equal(true)
|
||||
})
|
||||
|
||||
it('ignores updated lines', function () {
|
||||
this.spellCheckManager.changedLines[4] = false
|
||||
this.spellCheckManager.runSpellCheck()
|
||||
this.spellCheckManager.timeoutId = null
|
||||
this.$httpBackend.flush()
|
||||
expect(this.adapter.getLinesByRows).to.have.been.calledWith([3, 5])
|
||||
})
|
||||
|
||||
it('clears highlights for changed lines', function () {
|
||||
this.spellCheckManager.runSpellCheck()
|
||||
this.spellCheckManager.timeoutId = null
|
||||
this.$httpBackend.flush()
|
||||
expect(
|
||||
this.highlightedWordManager.clearRow.getCall(0).args[0]
|
||||
).to.equal(3)
|
||||
expect(
|
||||
this.highlightedWordManager.clearRow.getCall(1).args[0]
|
||||
).to.equal(4)
|
||||
expect(
|
||||
this.highlightedWordManager.clearRow.getCall(2).args[0]
|
||||
).to.equal(5)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('cache', function () {
|
||||
beforeEach(function () {
|
||||
this.adapter.getLineCount.returns(1)
|
||||
this.adapter.getFirstVisibleRowNum.returns(1)
|
||||
this.adapter.getLastVisibleRowNum.returns(1)
|
||||
this.adapter.getLinesByRows.returns(['Lorem ipsum dolor'])
|
||||
this.$httpBackend.when('POST', '/spelling/check').respond({
|
||||
misspellings: [
|
||||
{
|
||||
index: 0,
|
||||
suggestions: ['foobarbaz'],
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
it('adds already checked words to the spellchecker cache', function () {
|
||||
expect(this.spellCheckManager.cache.info().size).to.equal(0)
|
||||
this.spellCheckManager.init()
|
||||
this.timelord.tick(500)
|
||||
this.$httpBackend.flush()
|
||||
expect(this.spellCheckManager.cache.info().size).to.equal(3)
|
||||
})
|
||||
|
||||
it('adds misspeled word suggestions to the cache', function () {
|
||||
this.spellCheckManager.init()
|
||||
this.timelord.tick(500)
|
||||
this.$httpBackend.flush()
|
||||
|
||||
expect(
|
||||
this.spellCheckManager.cache.get(
|
||||
`${this.scope.spellCheckLanguage}:Lorem`
|
||||
)
|
||||
).to.eql(['foobarbaz'])
|
||||
})
|
||||
|
||||
it('adds non-misspeled words to the cache as a boolean', function () {
|
||||
this.spellCheckManager.init()
|
||||
this.timelord.tick(500)
|
||||
this.$httpBackend.flush()
|
||||
expect(
|
||||
this.spellCheckManager.cache.get(
|
||||
`${this.scope.spellCheckLanguage}:ipsum`
|
||||
)
|
||||
).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('backend', function () {
|
||||
beforeEach(function () {
|
||||
this.adapter.getLineCount.returns(1)
|
||||
this.adapter.getFirstVisibleRowNum.returns(1)
|
||||
this.adapter.getLastVisibleRowNum.returns(1)
|
||||
this.adapter.getLinesByRows.returns([
|
||||
'Lorem \\somecommand ipsum dolor \\othercommand',
|
||||
])
|
||||
})
|
||||
|
||||
it('hits the backend with all words at startup', function () {
|
||||
this.$httpBackend
|
||||
.expect('POST', '/spelling/check', {
|
||||
language: this.scope.spellCheckLanguage,
|
||||
words: ['Lorem', 'ipsum', 'dolor'],
|
||||
skipLearnedWords: true,
|
||||
token: window.user.id,
|
||||
_csrf: window.csrfToken,
|
||||
})
|
||||
.respond({
|
||||
misspellings: [
|
||||
{
|
||||
index: 0,
|
||||
suggestions: ['foobarbaz'],
|
||||
},
|
||||
],
|
||||
})
|
||||
this.spellCheckManager.init()
|
||||
this.timelord.tick(500)
|
||||
this.$httpBackend.flush()
|
||||
})
|
||||
|
||||
it('does not hit the backend when all words are already in the cache', function () {
|
||||
this.$httpBackend
|
||||
.expect('POST', '/spelling/check', {
|
||||
language: this.scope.spellCheckLanguage,
|
||||
words: ['Lorem', 'ipsum', 'dolor'],
|
||||
skipLearnedWords: true,
|
||||
token: window.user.id,
|
||||
_csrf: window.csrfToken,
|
||||
})
|
||||
.respond({
|
||||
misspellings: [
|
||||
{
|
||||
index: 0,
|
||||
suggestions: ['foobarbaz'],
|
||||
},
|
||||
],
|
||||
})
|
||||
this.spellCheckManager.init()
|
||||
this.timelord.tick(500)
|
||||
this.$httpBackend.flush()
|
||||
this.spellCheckManager.init()
|
||||
this.timelord.tick(500)
|
||||
})
|
||||
|
||||
it('hits the backend only with non-cached words', function () {
|
||||
this.$httpBackend
|
||||
.expect('POST', '/spelling/check', {
|
||||
language: this.scope.spellCheckLanguage,
|
||||
words: ['Lorem', 'ipsum', 'dolor'],
|
||||
skipLearnedWords: true,
|
||||
token: window.user.id,
|
||||
_csrf: window.csrfToken,
|
||||
})
|
||||
.respond({
|
||||
misspellings: [
|
||||
{
|
||||
index: 0,
|
||||
suggestions: ['foobarbaz'],
|
||||
},
|
||||
],
|
||||
})
|
||||
this.spellCheckManager.init()
|
||||
this.timelord.tick(500)
|
||||
this.$httpBackend.flush()
|
||||
|
||||
this.adapter.getLinesByRows.returns(['Lorem ipsum dolor sit amet'])
|
||||
this.$httpBackend
|
||||
.expect('POST', '/spelling/check', {
|
||||
language: this.scope.spellCheckLanguage,
|
||||
words: ['sit', 'amet'],
|
||||
skipLearnedWords: true,
|
||||
token: window.user.id,
|
||||
_csrf: window.csrfToken,
|
||||
})
|
||||
.respond({
|
||||
misspellings: [
|
||||
{
|
||||
index: 0,
|
||||
suggestions: ['bazbarfoo'],
|
||||
},
|
||||
],
|
||||
})
|
||||
this.spellCheckManager.init()
|
||||
this.timelord.tick(500)
|
||||
this.$httpBackend.flush()
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
this.$httpBackend.verifyNoOutstandingRequest()
|
||||
this.$httpBackend.verifyNoOutstandingExpectation()
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,649 +0,0 @@
|
|||
/* eslint-disable
|
||||
max-len,
|
||||
no-return-assign,
|
||||
*/
|
||||
/* global inject, sinon */
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
import HistoryV2Manager from '../../../../frontend/js/ide/history/HistoryV2Manager'
|
||||
|
||||
export default describe('HistoryV2Manager', function () {
|
||||
beforeEach(function (done) {
|
||||
this.defaultHistoryScope = {
|
||||
updates: [],
|
||||
viewMode: 'point_in_time',
|
||||
nextBeforeTimestamp: null,
|
||||
loading: false,
|
||||
atEnd: false,
|
||||
userHasFullFeature: undefined,
|
||||
freeHistoryLimitHit: false,
|
||||
selection: {
|
||||
docs: {},
|
||||
pathname: null,
|
||||
range: {
|
||||
fromV: null,
|
||||
toV: null,
|
||||
},
|
||||
hoveredRange: {
|
||||
fromV: null,
|
||||
toV: null,
|
||||
},
|
||||
diff: null,
|
||||
files: [],
|
||||
file: null,
|
||||
},
|
||||
error: null,
|
||||
showOnlyLabels: false,
|
||||
labels: null,
|
||||
loadingFileTree: true,
|
||||
isReact: false,
|
||||
}
|
||||
|
||||
this.sampleUpdates = [
|
||||
{
|
||||
fromV: 4,
|
||||
toV: 5,
|
||||
meta: {
|
||||
users: [
|
||||
{
|
||||
first_name: 'john.doe',
|
||||
last_name: '',
|
||||
email: 'john.doe@domain.tld',
|
||||
id: '5b57299087712202fb599ab4',
|
||||
hue: 200,
|
||||
},
|
||||
],
|
||||
start_ts: 1544021278346,
|
||||
end_ts: 1544021278346,
|
||||
},
|
||||
labels: [
|
||||
{
|
||||
id: '5c07e822042e67003b448f18',
|
||||
comment: 'My first label',
|
||||
version: 5,
|
||||
user_id: '5b57299087712202fb599ab4',
|
||||
created_at: '2018-12-05T15:00:50.688Z',
|
||||
},
|
||||
],
|
||||
pathnames: [],
|
||||
project_ops: [
|
||||
{
|
||||
add: {
|
||||
pathname: 'chapters/chapter1.tex',
|
||||
},
|
||||
atV: 4,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
fromV: 3,
|
||||
toV: 4,
|
||||
meta: {
|
||||
users: [
|
||||
{
|
||||
first_name: 'john.doe',
|
||||
last_name: '',
|
||||
email: 'john.doe@domain.tld',
|
||||
id: '5b57299087712202fb599ab4',
|
||||
hue: 200,
|
||||
},
|
||||
],
|
||||
start_ts: 1544021262622,
|
||||
end_ts: 1544021262622,
|
||||
},
|
||||
labels: [],
|
||||
pathnames: ['main.tex'],
|
||||
project_ops: [],
|
||||
},
|
||||
{
|
||||
fromV: 0,
|
||||
toV: 3,
|
||||
meta: {
|
||||
users: [
|
||||
{
|
||||
first_name: 'john.doe',
|
||||
last_name: '',
|
||||
email: 'john.doe@domain.tld',
|
||||
id: '5b57299087712202fb599ab4',
|
||||
hue: 200,
|
||||
},
|
||||
],
|
||||
start_ts: 1544021213540,
|
||||
end_ts: 1544021213618,
|
||||
},
|
||||
labels: [],
|
||||
pathnames: [],
|
||||
project_ops: [
|
||||
{
|
||||
add: {
|
||||
pathname: 'universe.jpg',
|
||||
},
|
||||
atV: 2,
|
||||
},
|
||||
{
|
||||
add: {
|
||||
pathname: 'references.bib',
|
||||
},
|
||||
atV: 1,
|
||||
},
|
||||
{
|
||||
add: {
|
||||
pathname: 'main.tex',
|
||||
},
|
||||
atV: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
inject(($q, $http, $filter, $rootScope) => {
|
||||
this.$scope = $rootScope.$new()
|
||||
this.$scope.project = {
|
||||
features: {
|
||||
versioning: true,
|
||||
},
|
||||
}
|
||||
this.$scope.user = {
|
||||
isAdmin: false,
|
||||
}
|
||||
this.ide = {
|
||||
globalEditorWatchdogManager: { attachToEditor() {} },
|
||||
$q,
|
||||
$http,
|
||||
$filter,
|
||||
}
|
||||
this.eventTracking = {
|
||||
sendMB: () => {},
|
||||
}
|
||||
this.localStorage = sinon.stub().returns(null)
|
||||
this.historyManager = new HistoryV2Manager(
|
||||
this.ide,
|
||||
this.$scope,
|
||||
this.localStorage,
|
||||
this.eventTracking
|
||||
)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should setup the history scope on initialization', function () {
|
||||
expect(this.$scope.history).to.deep.equal(this.defaultHistoryScope)
|
||||
})
|
||||
|
||||
it('should keep history updates after performing a soft reset', function () {
|
||||
const historyScopeWithUpdates = Object.assign(
|
||||
{},
|
||||
this.defaultHistoryScope,
|
||||
{
|
||||
updates: this.sampleUpdates,
|
||||
}
|
||||
)
|
||||
this.$scope.history.updates = this.sampleUpdates
|
||||
this.historyManager.softReset()
|
||||
expect(this.$scope.history).to.deep.equal(historyScopeWithUpdates)
|
||||
})
|
||||
|
||||
it('should discard history updates after performing a hard reset', function () {
|
||||
this.$scope.history.updates = this.sampleUpdates
|
||||
this.historyManager.hardReset()
|
||||
expect(this.$scope.history).to.deep.equal(this.defaultHistoryScope)
|
||||
})
|
||||
|
||||
it('should setup history with full access to the feature if the project has versioning', function () {
|
||||
this.$scope.$digest()
|
||||
expect(this.$scope.history.userHasFullFeature).to.equal(true)
|
||||
})
|
||||
|
||||
it('should setup history without full access to the feature if the project does not have versioning', function () {
|
||||
this.$scope.project.features.versioning = false
|
||||
this.historyManager = new HistoryV2Manager(
|
||||
this.ide,
|
||||
this.$scope,
|
||||
this.localStorage
|
||||
)
|
||||
this.$scope.$digest()
|
||||
expect(this.$scope.history.userHasFullFeature).to.equal(false)
|
||||
})
|
||||
|
||||
it('should setup history with full access to the feature for admin users even if the project does not have versioning', function () {
|
||||
this.$scope.project.features.versioning = false
|
||||
this.$scope.user.isAdmin = true
|
||||
this.historyManager = new HistoryV2Manager(
|
||||
this.ide,
|
||||
this.$scope,
|
||||
this.localStorage
|
||||
)
|
||||
this.$scope.$digest()
|
||||
expect(this.$scope.history.userHasFullFeature).to.equal(true)
|
||||
})
|
||||
|
||||
describe('autoSelectFile', function () {
|
||||
describe('for compare mode', function () {
|
||||
beforeEach(function () {
|
||||
this.mockedFilesList = [
|
||||
{
|
||||
pathname: 'main.tex',
|
||||
},
|
||||
{
|
||||
pathname: 'references.bib',
|
||||
},
|
||||
{
|
||||
pathname: 'universe.jpg',
|
||||
},
|
||||
{
|
||||
pathname: 'chapters/chapter2.tex',
|
||||
},
|
||||
{
|
||||
pathname: 'chapters/draft.tex',
|
||||
operation: 'removed',
|
||||
deletedAtV: 47,
|
||||
},
|
||||
{
|
||||
pathname: 'chapters/chapter3.tex',
|
||||
operation: 'added',
|
||||
},
|
||||
{
|
||||
pathname: 'chapters/chapter1.tex',
|
||||
operation: 'edited',
|
||||
},
|
||||
{
|
||||
pathname: 'chapters/foo.tex',
|
||||
oldPathname: 'chapters/bar.tex',
|
||||
operation: 'renamed',
|
||||
},
|
||||
]
|
||||
this.mockedMainTex = this.mockedFilesList[0]
|
||||
this.mockedReferencesFile = this.mockedFilesList[1]
|
||||
this.mockedRemovedFile = this.mockedFilesList[4]
|
||||
this.mockedAddedFile = this.mockedFilesList[5]
|
||||
this.mockedEditedFile = this.mockedFilesList[6]
|
||||
this.mockedRenamedFile = this.mockedFilesList[7]
|
||||
|
||||
this.$scope.history.viewMode = 'compare'
|
||||
this.$scope.history.selection.files = this.mockedFilesList
|
||||
})
|
||||
|
||||
describe('with a previously selected file', function () {
|
||||
it('should prefer the previously selected file if it is available and has operations', function () {
|
||||
this.historyManager._previouslySelectedPathname =
|
||||
this.mockedAddedFile.pathname
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file).to.deep.equal(
|
||||
this.mockedAddedFile
|
||||
)
|
||||
})
|
||||
|
||||
it('should prefer a file with ops if the previously selected file is available but has no operations', function () {
|
||||
this.historyManager._previouslySelectedPathname =
|
||||
this.mockedReferencesFile.pathname
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file.operation).to.exist
|
||||
})
|
||||
|
||||
it('should ignore the previously selected file if it is not available', function () {
|
||||
this.historyManager._previouslySelectedPathname = 'non/existent.file'
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file.pathname).to.not.equal(
|
||||
'non/existent.file'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('without a previously selected file, with a list of files containing operations', function () {
|
||||
it('should prefer edited files', function () {
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file).to.deep.equal(
|
||||
this.mockedEditedFile
|
||||
)
|
||||
})
|
||||
|
||||
it('should prefer added files if no edited files are present', function () {
|
||||
const indexOfEditedFile = this.$scope.history.selection.files.indexOf(
|
||||
this.mockedEditedFile
|
||||
)
|
||||
this.$scope.history.selection.files.splice(indexOfEditedFile, 1)
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file).to.deep.equal(
|
||||
this.mockedAddedFile
|
||||
)
|
||||
})
|
||||
|
||||
it('should prefer renamed files if no edited or added files are present', function () {
|
||||
const indexOfEditedFile = this.$scope.history.selection.files.indexOf(
|
||||
this.mockedEditedFile
|
||||
)
|
||||
this.$scope.history.selection.files.splice(indexOfEditedFile, 1)
|
||||
const indexOfAddedFile = this.$scope.history.selection.files.indexOf(
|
||||
this.mockedAddedFile
|
||||
)
|
||||
this.$scope.history.selection.files.splice(indexOfAddedFile, 1)
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file).to.deep.equal(
|
||||
this.mockedRenamedFile
|
||||
)
|
||||
})
|
||||
|
||||
it('should prefer removed files if no edited, added or renamed files are present', function () {
|
||||
const indexOfEditedFile = this.$scope.history.selection.files.indexOf(
|
||||
this.mockedEditedFile
|
||||
)
|
||||
this.$scope.history.selection.files.splice(indexOfEditedFile, 1)
|
||||
const indexOfAddedFile = this.$scope.history.selection.files.indexOf(
|
||||
this.mockedAddedFile
|
||||
)
|
||||
this.$scope.history.selection.files.splice(indexOfAddedFile, 1)
|
||||
const indexOfRenamedFile =
|
||||
this.$scope.history.selection.files.indexOf(this.mockedRenamedFile)
|
||||
this.$scope.history.selection.files.splice(indexOfRenamedFile, 1)
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file).to.deep.equal(
|
||||
this.mockedRemovedFile
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('without a previously selected file, with a list of files without operations', function () {
|
||||
beforeEach(function () {
|
||||
this.mockedFilesListWithNoOps = [
|
||||
{
|
||||
pathname: 'main.tex',
|
||||
},
|
||||
{
|
||||
pathname: 'references.bib',
|
||||
},
|
||||
{
|
||||
pathname: 'other.tex',
|
||||
},
|
||||
{
|
||||
pathname: 'universe.jpg',
|
||||
},
|
||||
]
|
||||
this.mockedMainTex = this.mockedFilesListWithNoOps[0]
|
||||
this.mockedReferencesFile = this.mockedFilesListWithNoOps[1]
|
||||
this.mockedOtherTexFile = this.mockedFilesListWithNoOps[2]
|
||||
|
||||
this.$scope.history.selection.files = this.mockedFilesListWithNoOps
|
||||
})
|
||||
|
||||
it('should prefer main.tex if it exists', function () {
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file).to.deep.equal(
|
||||
this.mockedMainTex
|
||||
)
|
||||
})
|
||||
|
||||
it('should prefer another tex file if main.tex does not exist', function () {
|
||||
const indexOfMainTex = this.$scope.history.selection.files.indexOf(
|
||||
this.mockedMainTex
|
||||
)
|
||||
this.$scope.history.selection.files.splice(indexOfMainTex, 1)
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file).to.deep.equal(
|
||||
this.mockedOtherTexFile
|
||||
)
|
||||
})
|
||||
|
||||
it('should pick the first available file if no tex files are available', function () {
|
||||
const indexOfMainTex = this.$scope.history.selection.files.indexOf(
|
||||
this.mockedMainTex
|
||||
)
|
||||
this.$scope.history.selection.files.splice(indexOfMainTex, 1)
|
||||
const indexOfOtherTexFile =
|
||||
this.$scope.history.selection.files.indexOf(this.mockedOtherTexFile)
|
||||
this.$scope.history.selection.files.splice(indexOfOtherTexFile, 1)
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file).to.deep.equal(
|
||||
this.mockedReferencesFile
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('for point-in-time mode', function () {
|
||||
beforeEach(function () {
|
||||
this.$scope.history.viewMode = 'point_in_time'
|
||||
this.sampleUpdates[0] = {
|
||||
fromV: 4,
|
||||
toV: 5,
|
||||
meta: {
|
||||
users: [
|
||||
{
|
||||
first_name: 'john.doe',
|
||||
last_name: '',
|
||||
email: 'john.doe@domain.tld',
|
||||
id: '5b57299087712202fb599ab4',
|
||||
hue: 200,
|
||||
},
|
||||
],
|
||||
start_ts: 1544021278346,
|
||||
end_ts: 1544021278346,
|
||||
},
|
||||
pathnames: ['main.tex'],
|
||||
project_ops: [
|
||||
{
|
||||
add: {
|
||||
pathname: 'chapters/chapter1.tex',
|
||||
},
|
||||
atV: 4,
|
||||
},
|
||||
{
|
||||
rename: {
|
||||
pathname: 'foo.tex',
|
||||
newPathname: 'bar.tex',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
this.sampleUpdateEditedFile = this.sampleUpdates[0].pathnames[0]
|
||||
this.sampleUpdateAddedFile =
|
||||
this.sampleUpdates[0].project_ops[0].add.pathname
|
||||
this.sampleUpdateRenamedFile =
|
||||
this.sampleUpdates[0].project_ops[1].rename.newPathname
|
||||
this.$scope.history.updates = this.sampleUpdates
|
||||
this.$scope.history.selection.range = {
|
||||
fromV: this.sampleUpdates[0].toV,
|
||||
toV: this.sampleUpdates[0].toV,
|
||||
}
|
||||
this.mockedFilesList = [
|
||||
{
|
||||
pathname: 'main.tex',
|
||||
},
|
||||
{
|
||||
pathname: 'references.bib',
|
||||
},
|
||||
{
|
||||
pathname: 'universe.jpg',
|
||||
},
|
||||
{
|
||||
pathname: 'chapters/chapter2.tex',
|
||||
},
|
||||
{
|
||||
pathname: 'chapters/draft.tex',
|
||||
},
|
||||
{
|
||||
pathname: 'chapters/chapter3.tex',
|
||||
},
|
||||
{
|
||||
pathname: 'chapters/chapter1.tex',
|
||||
},
|
||||
{
|
||||
pathname: 'bar.tex',
|
||||
},
|
||||
]
|
||||
this.$scope.history.selection.files = this.mockedFilesList
|
||||
})
|
||||
|
||||
describe('with a previously selected file', function () {
|
||||
it('should prefer the previously selected file if it is available and has operations', function () {
|
||||
this.historyManager._previouslySelectedPathname = 'main.tex'
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file.pathname).to.equal(
|
||||
this.sampleUpdateEditedFile
|
||||
)
|
||||
})
|
||||
|
||||
it('should prefer a file with ops if the previously selected file is available but has no operations', function () {
|
||||
this.historyManager._previouslySelectedPathname = 'main.tex'
|
||||
this.sampleUpdates[0].pathnames = []
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file.pathname).to.equal(
|
||||
this.sampleUpdateAddedFile
|
||||
)
|
||||
})
|
||||
|
||||
it('should ignore the previously selected file if it is not available', function () {
|
||||
this.historyManager._previouslySelectedPathname = 'non/existent.file'
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file.pathname).to.not.equal(
|
||||
'non/existent.file'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('without a previously selected file, with a list of files containing operations', function () {
|
||||
it('should prefer edited files', function () {
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file.pathname).to.equal(
|
||||
this.sampleUpdateEditedFile
|
||||
)
|
||||
})
|
||||
|
||||
it('should prefer added files if no edited files are present', function () {
|
||||
this.sampleUpdates[0].pathnames = []
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file.pathname).to.equal(
|
||||
this.sampleUpdateAddedFile
|
||||
)
|
||||
})
|
||||
|
||||
it('should prefer renamed files if no edited or added files are present', function () {
|
||||
this.sampleUpdates[0].pathnames = []
|
||||
this.sampleUpdates[0].project_ops.shift()
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file.pathname).to.equal(
|
||||
this.sampleUpdateRenamedFile
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('without a previously selected file, with a list of files without operations', function () {
|
||||
beforeEach(function () {
|
||||
this.sampleUpdates[0].pathnames = []
|
||||
this.sampleUpdates[0].project_ops = []
|
||||
this.mockedMainTex = this.mockedFilesList[0]
|
||||
this.mockedReferencesFile = this.mockedFilesList[1]
|
||||
})
|
||||
|
||||
it('should prefer main.tex if it exists', function () {
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file.pathname).to.equal(
|
||||
this.mockedMainTex.pathname
|
||||
)
|
||||
})
|
||||
|
||||
it('should prefer another tex file if main.tex does not exist', function () {
|
||||
const indexOfMainTex = this.$scope.history.selection.files.indexOf(
|
||||
this.mockedMainTex
|
||||
)
|
||||
this.$scope.history.selection.files.splice(indexOfMainTex, 1)
|
||||
this.historyManager.autoSelectFile()
|
||||
expect(this.$scope.history.selection.file.pathname).to.match(/.tex$/)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('_loadLabels', function () {
|
||||
it('should return labels list as is if there is a label for the last version', function () {
|
||||
const labels = [
|
||||
{
|
||||
id: '1',
|
||||
version: 1,
|
||||
comment: 'foo',
|
||||
created_at: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
version: 2,
|
||||
comment: 'bar',
|
||||
created_at: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
version: 3,
|
||||
comment: 'baz',
|
||||
created_at: new Date().toISOString(),
|
||||
},
|
||||
]
|
||||
const lastUpdate = 3
|
||||
|
||||
const labelsResult = this.historyManager._loadLabels(labels, lastUpdate)
|
||||
|
||||
expect(labelsResult).to.have.members(labels)
|
||||
})
|
||||
|
||||
it('should return a labels list with a pseudo current state label if there is no label for the last version', function () {
|
||||
const labels = [
|
||||
{
|
||||
id: '1',
|
||||
version: 1,
|
||||
comment: 'foo',
|
||||
created_at: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
version: 2,
|
||||
comment: 'bar',
|
||||
created_at: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
version: 3,
|
||||
comment: 'baz',
|
||||
created_at: new Date().toISOString(),
|
||||
},
|
||||
]
|
||||
const lastUpdate = 5
|
||||
|
||||
const labelsResult = this.historyManager._loadLabels(labels, lastUpdate)
|
||||
|
||||
expect(labelsResult).to.include.members(labels)
|
||||
expect(labelsResult[0].isPseudoCurrentStateLabel).to.equal(true)
|
||||
expect(labelsResult[0].version).to.equal(5)
|
||||
})
|
||||
|
||||
it('should keep pseudo label when deleting label', function () {
|
||||
this.historyManager.$scope.history.labels = [
|
||||
{
|
||||
id: '1',
|
||||
version: 1,
|
||||
comment: 'foo',
|
||||
created_at: new Date().toISOString(),
|
||||
},
|
||||
]
|
||||
const lastUpdate = 5
|
||||
|
||||
this.historyManager.$scope.history.labels =
|
||||
this.historyManager._loadLabels(
|
||||
this.historyManager.$scope.history.labels,
|
||||
lastUpdate
|
||||
)
|
||||
|
||||
expect(
|
||||
this.historyManager.$scope.history.labels[0].isPseudoCurrentStateLabel
|
||||
).to.equal(true)
|
||||
|
||||
this.historyManager.$scope.history.labels =
|
||||
this.historyManager._loadLabels([], lastUpdate)
|
||||
expect(
|
||||
this.historyManager.$scope.history.labels[0].isPseudoCurrentStateLabel
|
||||
).to.equal(true)
|
||||
expect(this.historyManager.$scope.history.labels[0].version).to.equal(5)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,45 +0,0 @@
|
|||
/* global chai */
|
||||
|
||||
// Allow for mocking of Angular
|
||||
import 'angular'
|
||||
import 'angular-mocks'
|
||||
|
||||
/**
|
||||
* Add chai assertion for comparing CodeMirror Pos objects.
|
||||
* A deep comparison will fail because CodeMirror inserts additional properties
|
||||
* that we want to ignore.
|
||||
*/
|
||||
chai.Assertion.addMethod('equalPos', function (expectedPos) {
|
||||
const { line: actualLine, ch: actualCh } = this._obj
|
||||
const { line: expectedLine, ch: expectedCh } = expectedPos
|
||||
|
||||
this.assert(
|
||||
actualLine === expectedLine && actualCh === expectedCh,
|
||||
`expected #{exp} to equal #{act}`,
|
||||
`expected #{exp} to not equal #{act}`,
|
||||
`Pos({ line: ${expectedLine}, ch: ${expectedCh} })`,
|
||||
`Pos({ line: ${actualLine}, ch: ${actualCh} })`
|
||||
)
|
||||
})
|
||||
|
||||
// Mock ExposedSettings
|
||||
window.ExposedSettings = {}
|
||||
|
||||
/*
|
||||
* Bundle all test files together into a single bundle, and run tests against
|
||||
* this single bundle.
|
||||
* We are using karma-webpack to bundle our tests and the 'default' strategy is
|
||||
* to create a bundle for each test file. This isolates the tests better, but
|
||||
* causes a problem with Angular. The issue with Angular tests is because we
|
||||
* load a single global copy of Angular (see karma.conf.js) but
|
||||
* frontend/js/base.js is included in each bundle, meaning the Angular app is
|
||||
* initialised for each bundle when it is loaded onto the page when Karma
|
||||
* starts. This means that only the last bundle will have controllers/directives
|
||||
* registered against it, ultimately meaning that all other bundles will fail
|
||||
* because Angular cannot find the controller/directive under test.
|
||||
*/
|
||||
|
||||
// Import from the top-level any JS files within a test/karma
|
||||
// directory
|
||||
const context = require.context('../../', true, /test\/karma\/.*\.js$/)
|
||||
context.keys().forEach(context)
|
|
@ -1,18 +0,0 @@
|
|||
class Fixture {
|
||||
constructor() {
|
||||
this.el = document.createElement('div')
|
||||
document.body.appendChild(this.el)
|
||||
}
|
||||
|
||||
load(html) {
|
||||
this.el.innerHTML = html
|
||||
return this.el.firstChild
|
||||
}
|
||||
|
||||
cleanUp() {
|
||||
this.el.innerHTML = ''
|
||||
}
|
||||
}
|
||||
|
||||
const fixture = new Fixture()
|
||||
export default fixture
|
|
@ -1,16 +0,0 @@
|
|||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||
const { merge } = require('webpack-merge')
|
||||
|
||||
const base = require('./webpack.config')
|
||||
|
||||
const config = merge(base, {
|
||||
mode: 'development',
|
||||
|
||||
plugins: [new MiniCssExtractPlugin()],
|
||||
})
|
||||
|
||||
// Karma configures entry & output for us, so disable these
|
||||
delete config.entry
|
||||
delete config.output
|
||||
|
||||
module.exports = config
|
Loading…
Reference in a new issue