Преглед изворни кода

Merge pull request #15006 from overleaf/mj-bad-karma

[web] Remove karma tests and config

GitOrigin-RevId: a157f46d1ecf93fcbc99713a1c3aa11f82e5a619
Jakob Ackermann пре 2 година
родитељ
комит
6b3dac803d

Разлика између датотеке није приказан због своје велике величине
+ 0 - 763
package-lock.json


+ 0 - 13
patches/finalhandler+1.1.2.patch

@@ -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
-     }
- 

+ 0 - 9
services/web/.eslintrc

@@ -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"],

+ 6 - 23
services/web/Makefile

@@ -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

+ 0 - 14
services/web/docker-compose.ci.yml

@@ -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: ../..

+ 0 - 13
services/web/docker-compose.yml

@@ -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: ../..

+ 0 - 65
services/web/karma.conf.js

@@ -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()
-  }
-}

+ 1 - 10
services/web/package.json

@@ -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",

+ 0 - 308
services/web/test/karma/ide/editor/aceEditor/spell-check/SpellCheckManagerTests.js

@@ -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()
-    })
-  })
-})

+ 0 - 649
services/web/test/karma/ide/history/HistoryV2ManagerTests.js

@@ -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)
-      })
-    })
-  })
-})

+ 0 - 45
services/web/test/karma/import_tests.js

@@ -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)

+ 0 - 18
services/web/test/karma/support/fixture.js

@@ -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

+ 0 - 16
services/web/webpack.config.test.js

@@ -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

Неке датотеке нису приказане због велике количине промена