mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -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/src/**/*.js",
|
||||||
"app.js",
|
"app.js",
|
||||||
"i18next-scanner.config.js",
|
"i18next-scanner.config.js",
|
||||||
"karma.conf.js",
|
|
||||||
"scripts/**/*.js",
|
"scripts/**/*.js",
|
||||||
"webpack.config*.js"
|
"webpack.config*.js"
|
||||||
],
|
],
|
||||||
|
@ -82,14 +81,6 @@
|
||||||
"mocha/prefer-arrow-callback": "error"
|
"mocha/prefer-arrow-callback": "error"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
// Frontend test specific rules
|
|
||||||
"files": ["**/test/karma/**/*.js"],
|
|
||||||
"globals": {
|
|
||||||
"expect": true,
|
|
||||||
"$": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
// Backend specific rules
|
// Backend specific rules
|
||||||
"files": ["**/app/src/**/*.js", "app.js"],
|
"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_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_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=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=frontend_test_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) down --rmi local
|
||||||
-COMPOSE_PROJECT_NAME=tar_$(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
|
# 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
|
test_module: test_unit_module test_acceptance_module
|
||||||
|
|
||||||
|
@ -115,18 +113,6 @@ test_unit_modules: $(TEST_UNIT_MODULES)
|
||||||
test_unit_module:
|
test_unit_module:
|
||||||
$(MAKE) modules/$(MODULE_NAME)/test_unit
|
$(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
|
# Frontend tests
|
||||||
|
@ -445,7 +431,7 @@ lint_test_modules:
|
||||||
--max-warnings=0
|
--max-warnings=0
|
||||||
|
|
||||||
lint: lint_misc
|
lint: lint_misc
|
||||||
# migrations, scripts, webpack config, karma config
|
# migrations, scripts, webpack config
|
||||||
lint_misc:
|
lint_misc:
|
||||||
npx eslint . \
|
npx eslint . \
|
||||||
--ignore-pattern 'app.{js,jsx,mjs,ts,tsx}' \
|
--ignore-pattern 'app.{js,jsx,mjs,ts,tsx}' \
|
||||||
|
@ -558,9 +544,6 @@ build:
|
||||||
--file Dockerfile \
|
--file Dockerfile \
|
||||||
../..
|
../..
|
||||||
|
|
||||||
build_test_karma:
|
|
||||||
COMPOSE_PROJECT_NAME=karma_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) build test_karma
|
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
docker push $(DOCKER_REPO)/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER)
|
docker push $(DOCKER_REPO)/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER)
|
||||||
|
|
||||||
|
@ -583,7 +566,7 @@ $(MODULE_TARGETS):
|
||||||
$(MODULE_TARGETS) \
|
$(MODULE_TARGETS) \
|
||||||
compile_modules compile_modules_full clean_ci \
|
compile_modules compile_modules_full clean_ci \
|
||||||
test test_module test_unit test_unit_app \
|
test test_module test_unit test_unit_app \
|
||||||
test_unit_modules test_unit_module test_karma test_karma_run \
|
test_unit_modules test_unit_module test_frontend \
|
||||||
test_karma_build_run test_frontend test_acceptance test_acceptance_app \
|
test_acceptance test_acceptance_app test_acceptance_modules \
|
||||||
test_acceptance_modules test_acceptance_module ci format format_fix lint \
|
test_acceptance_module ci format format_fix lint \
|
||||||
build build_test_karma publish tar
|
build publish tar
|
||||||
|
|
|
@ -38,20 +38,6 @@ services:
|
||||||
- saml
|
- saml
|
||||||
- ldap
|
- 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:
|
test_frontend:
|
||||||
build:
|
build:
|
||||||
context: ../..
|
context: ../..
|
||||||
|
|
|
@ -46,19 +46,6 @@ services:
|
||||||
- ldap
|
- ldap
|
||||||
command: npm run --silent test:acceptance:app
|
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:
|
test_frontend:
|
||||||
build:
|
build:
|
||||||
context: ../..
|
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: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": "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: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",
|
"start": "node $NODE_APP_OPTIONS app.js",
|
||||||
"nodemon": "node --watch $NODE_APP_OPTIONS app.js --watch-locales",
|
"nodemon": "node --watch $NODE_APP_OPTIONS app.js --watch-locales",
|
||||||
"webpack": "webpack serve --config webpack.config.dev.js",
|
"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:acceptance": "npm run local:test:acceptance:app && npm run local:test:acceptance:modules",
|
||||||
"local:test:unit": "npm run test:unit:all",
|
"local:test:unit": "npm run test:unit:all",
|
||||||
"local:test:frontend": "npm run test:frontend",
|
"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": [
|
"browserslist": [
|
||||||
"last 1 year",
|
"last 1 year",
|
||||||
|
@ -289,13 +287,6 @@
|
||||||
"jquery": "^2.2.4",
|
"jquery": "^2.2.4",
|
||||||
"jsdom": "^19.0.0",
|
"jsdom": "^19.0.0",
|
||||||
"jsdom-global": "^3.0.2",
|
"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": "^3.11.1",
|
||||||
"less-loader": "^11.1.3",
|
"less-loader": "^11.1.3",
|
||||||
"match-sorter": "^6.2.0",
|
"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