From 0fe343f7d3fb53306b24792d1857a805bc728337 Mon Sep 17 00:00:00 2001
From: Konstantin Schulz
Date: Wed, 25 Mar 2020 12:04:10 +0100
Subject: [PATCH] added semantics page to display vector networks for the
Panegyrici Latini corpus
---
.gitlab-ci.yml | 1 -
angular.json | 2 +-
package-lock.json | 388 +++++++++++-------
package.json | 15 +-
src/app/app-routing.module.ts | 4 +
src/app/app.component.html | 25 +-
src/app/app.component.spec.ts | 35 +-
src/app/app.component.ts | 1 -
src/app/author-detail/author-detail.page.html | 8 +-
.../author-detail/author-detail.page.spec.ts | 16 +-
src/app/author-detail/author-detail.page.ts | 8 +-
src/app/author/author.page.html | 6 +-
src/app/author/author.page.spec.ts | 65 ++-
src/app/author/author.page.ts | 58 +--
.../confirm-cancel.page.spec.ts | 15 +-
src/app/confirm-cancel/confirm-cancel.page.ts | 22 +-
src/app/corpus.service.spec.ts | 379 ++++++++++++++++-
src/app/corpus.service.ts | 208 +++++-----
src/app/doc-exercises/doc-exercises.page.html | 12 +-
src/app/doc-exercises/doc-exercises.page.ts | 4 +-
src/app/doc-software/doc-software.page.html | 12 +-
src/app/doc-software/doc-software.page.ts | 4 +-
src/app/doc-voc-unit/doc-voc-unit.page.html | 12 +-
src/app/doc-voc-unit/doc-voc-unit.page.ts | 4 +-
src/app/exercise-list/exercise-list.page.html | 10 +-
.../exercise-list/exercise-list.page.spec.ts | 102 ++++-
src/app/exercise-list/exercise-list.page.ts | 91 ++--
.../exercise-parameters.page.html | 4 +-
.../exercise-parameters.page.spec.ts | 124 +++++-
.../exercise-parameters.page.ts | 123 +++---
src/app/exercise.service.spec.ts | 51 ++-
src/app/exercise.service.ts | 27 +-
src/app/exercise/exercise.page.html | 4 +-
src/app/exercise/exercise.page.spec.ts | 69 +++-
src/app/exercise/exercise.page.ts | 88 ++--
src/app/helper.service.spec.ts | 190 ++++++++-
src/app/helper.service.ts | 308 ++++++++------
src/app/home/home.page.html | 18 +-
src/app/home/home.page.spec.ts | 49 ++-
src/app/home/home.page.ts | 62 +--
src/app/imprint/imprint.page.html | 12 +-
src/app/imprint/imprint.page.ts | 24 +-
src/app/info/info.page.html | 12 +-
src/app/info/info.page.ts | 6 +-
src/app/kwic/kwic.page.html | 7 +-
src/app/kwic/kwic.page.ts | 16 +-
src/app/models/h5pEventDispatcherMock.ts | 30 ++
src/app/models/mock.ts | 20 -
src/app/models/mockMC.ts | 61 +++
src/app/models/xAPI/Activity.ts | 12 +-
src/app/models/xAPI/Context.ts | 16 +-
src/app/models/xAPI/ContextActivities.ts | 6 +-
src/app/models/xAPI/Definition.ts | 6 +-
src/app/models/xAPI/Result.ts | 16 +-
src/app/models/xAPI/Score.ts | 6 +-
src/app/models/xAPI/StatementBase.ts | 18 +-
src/app/models/xAPI/Verb.ts | 10 +-
src/app/preview/preview.page.html | 12 +-
src/app/preview/preview.page.spec.ts | 146 ++++++-
src/app/preview/preview.page.ts | 85 ++--
src/app/ranking/ranking.page.html | 4 +-
src/app/ranking/ranking.page.spec.ts | 33 +-
src/app/ranking/ranking.page.ts | 28 +-
src/app/semantics/semantics-routing.module.ts | 17 +
src/app/semantics/semantics.module.ts | 23 ++
src/app/semantics/semantics.page.html | 74 ++++
src/app/semantics/semantics.page.scss | 0
src/app/semantics/semantics.page.spec.ts | 74 ++++
src/app/semantics/semantics.page.ts | 85 ++++
src/app/show-text/show-text.page.html | 10 +-
src/app/show-text/show-text.page.spec.ts | 51 ++-
src/app/show-text/show-text.page.ts | 66 ++-
src/app/sources/sources.page.html | 8 +-
src/app/sources/sources.page.ts | 26 +-
src/app/test/test.page.html | 20 +-
src/app/test/test.page.spec.ts | 264 +++++++++++-
src/app/test/test.page.ts | 250 ++++++-----
src/app/text-range/text-range.page.html | 6 +-
src/app/text-range/text-range.page.spec.ts | 8 +-
src/app/text-range/text-range.page.ts | 18 +-
.../translate-testing.module.ts | 10 +-
.../vocabulary-check.page.html | 4 +-
.../vocabulary-check/vocabulary-check.page.ts | 18 +-
src/app/vocabulary.service.spec.ts | 22 +-
src/app/vocabulary.service.ts | 6 +-
src/assets/i18n/de.json | 9 +-
src/assets/i18n/en.json | 9 +-
src/configMC.ts | 23 +-
src/karma.conf.js | 7 +-
src/polyfills.ts | 10 +-
src/tsconfig.app.json | 21 +-
tsconfig.json | 6 +-
92 files changed, 3166 insertions(+), 1189 deletions(-)
create mode 100644 src/app/models/h5pEventDispatcherMock.ts
delete mode 100644 src/app/models/mock.ts
create mode 100644 src/app/models/mockMC.ts
create mode 100644 src/app/semantics/semantics-routing.module.ts
create mode 100644 src/app/semantics/semantics.module.ts
create mode 100644 src/app/semantics/semantics.page.html
create mode 100644 src/app/semantics/semantics.page.scss
create mode 100644 src/app/semantics/semantics.page.spec.ts
create mode 100644 src/app/semantics/semantics.page.ts
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5a9c61d..d0efc9e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,6 +1,5 @@
stages:
- test
- - deploy
coverage:
stage: test
script:
diff --git a/angular.json b/angular.json
index 493ea8b..dbbca11 100644
--- a/angular.json
+++ b/angular.json
@@ -12,7 +12,7 @@
"schematics": {},
"architect": {
"build": {
- "builder": "@angular-devkit/build-angular:browser",
+ "builder": "@angular-builders/custom-webpack:browser",
"options": {
"outputPath": "www",
"index": "src/index.html",
diff --git a/package-lock.json b/package-lock.json
index 66ea230..fe6181b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "mc_frontend",
- "version": "1.5.7",
+ "version": "1.7.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -94,6 +94,27 @@
"worker-plugin": "3.2.0"
},
"dependencies": {
+ "coverage-istanbul-loader": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/coverage-istanbul-loader/-/coverage-istanbul-loader-2.0.3.tgz",
+ "integrity": "sha512-LiGRvyIuzVYs3M1ZYK1tF0HekjH0DJ8zFdUwAZq378EJzqOgToyb1690dp3TAUlP6Y+82uu42LRjuROVeJ54CA==",
+ "requires": {
+ "convert-source-map": "^1.7.0",
+ "istanbul-lib-instrument": "^4.0.0",
+ "loader-utils": "^1.2.3",
+ "merge-source-map": "^1.1.0",
+ "schema-utils": "^2.6.1"
+ }
+ },
+ "schema-utils": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.4.tgz",
+ "integrity": "sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ==",
+ "requires": {
+ "ajv": "^6.10.2",
+ "ajv-keywords": "^3.4.1"
+ }
+ },
"webpack-merge": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz",
@@ -1512,6 +1533,31 @@
"integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==",
"dev": true
},
+ "coverage-istanbul-loader": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/coverage-istanbul-loader/-/coverage-istanbul-loader-2.0.3.tgz",
+ "integrity": "sha512-LiGRvyIuzVYs3M1ZYK1tF0HekjH0DJ8zFdUwAZq378EJzqOgToyb1690dp3TAUlP6Y+82uu42LRjuROVeJ54CA==",
+ "dev": true,
+ "requires": {
+ "convert-source-map": "^1.7.0",
+ "istanbul-lib-instrument": "^4.0.0",
+ "loader-utils": "^1.2.3",
+ "merge-source-map": "^1.1.0",
+ "schema-utils": "^2.6.1"
+ },
+ "dependencies": {
+ "schema-utils": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.4.tgz",
+ "integrity": "sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.10.2",
+ "ajv-keywords": "^3.4.1"
+ }
+ }
+ }
+ },
"find-cache-dir": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.0.0.tgz",
@@ -3715,9 +3761,9 @@
}
},
"@types/jasmine": {
- "version": "2.8.16",
- "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.16.tgz",
- "integrity": "sha512-056oRlBBp7MDzr+HoU5su099s/s7wjZ3KcHxLfv+Byqb9MwdLUvsfLgw1VS97hsh3ddxSPyQu+olHMnoVTUY6g==",
+ "version": "3.5.9",
+ "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.9.tgz",
+ "integrity": "sha512-KNL2Fq6GRmty2j6+ZmueT/Z/dkctLNH+5DFoGHNDtcgt7yME9NZd8x2p81Yuea1Xux/qAryDd3zVLUoKpDz1TA==",
"dev": true
},
"@types/jasminewd2": {
@@ -5349,38 +5395,15 @@
}
},
"connect": {
- "version": "3.6.6",
- "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz",
- "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=",
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
+ "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
"dev": true,
"requires": {
"debug": "2.6.9",
- "finalhandler": "1.1.0",
- "parseurl": "~1.3.2",
+ "finalhandler": "1.1.2",
+ "parseurl": "~1.3.3",
"utils-merge": "1.0.1"
- },
- "dependencies": {
- "finalhandler": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
- "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=",
- "dev": true,
- "requires": {
- "debug": "2.6.9",
- "encodeurl": "~1.0.1",
- "escape-html": "~1.0.3",
- "on-finished": "~2.3.0",
- "parseurl": "~1.3.2",
- "statuses": "~1.3.1",
- "unpipe": "~1.0.0"
- }
- },
- "statuses": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
- "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=",
- "dev": true
- }
}
},
"connect-history-api-fallback": {
@@ -6219,9 +6242,9 @@
"integrity": "sha512-EYC5eQFVkoYXq39l7tYKE6lEjHJ04mvTmKXxGL7quHLdFPfJMNzru/UYpn92AOfpl3PQaZmou78C7EgmFOwFQQ=="
},
"core-js": {
- "version": "2.6.11",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
- "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="
+ "version": "3.6.4",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz",
+ "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw=="
},
"core-js-compat": {
"version": "3.6.4",
@@ -6266,69 +6289,6 @@
}
}
},
- "coverage-istanbul-loader": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/coverage-istanbul-loader/-/coverage-istanbul-loader-2.0.3.tgz",
- "integrity": "sha512-LiGRvyIuzVYs3M1ZYK1tF0HekjH0DJ8zFdUwAZq378EJzqOgToyb1690dp3TAUlP6Y+82uu42LRjuROVeJ54CA==",
- "requires": {
- "convert-source-map": "^1.7.0",
- "istanbul-lib-instrument": "^4.0.0",
- "loader-utils": "^1.2.3",
- "merge-source-map": "^1.1.0",
- "schema-utils": "^2.6.1"
- },
- "dependencies": {
- "ajv": {
- "version": "6.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
- "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
- "requires": {
- "fast-deep-equal": "^3.1.1",
- "fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.2"
- }
- },
- "fast-deep-equal": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
- "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
- },
- "istanbul-lib-coverage": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz",
- "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg=="
- },
- "istanbul-lib-instrument": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz",
- "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==",
- "requires": {
- "@babel/core": "^7.7.5",
- "@babel/parser": "^7.7.5",
- "@babel/template": "^7.7.4",
- "@babel/traverse": "^7.7.4",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-coverage": "^3.0.0",
- "semver": "^6.3.0"
- }
- },
- "schema-utils": {
- "version": "2.6.4",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.4.tgz",
- "integrity": "sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ==",
- "requires": {
- "ajv": "^6.10.2",
- "ajv-keywords": "^3.4.1"
- }
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
- }
- }
- },
"create-ecdh": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
@@ -6602,9 +6562,9 @@
}
},
"date-format": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.0.0.tgz",
- "integrity": "sha512-M6UqVvZVgFYqZL1SfHsRGIQSz3ZL+qgbsV5Lp1Vj61LZVYuEwcMXYay7DRDtYs2HQQBK5hQtQ0fD9aEJ89V0LA==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz",
+ "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==",
"dev": true
},
"debug": {
@@ -7642,9 +7602,9 @@
}
},
"flatted": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz",
- "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
+ "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
"dev": true
},
"flush-write-stream": {
@@ -9284,6 +9244,11 @@
}
}
},
+ "istanbul-lib-coverage": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz",
+ "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg=="
+ },
"istanbul-lib-hook": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz",
@@ -9293,6 +9258,27 @@
"append-transform": "^1.0.0"
}
},
+ "istanbul-lib-instrument": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz",
+ "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==",
+ "requires": {
+ "@babel/core": "^7.7.5",
+ "@babel/parser": "^7.7.5",
+ "@babel/template": "^7.7.4",
+ "@babel/traverse": "^7.7.4",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.0.0",
+ "semver": "^6.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+ }
+ }
+ },
"istanbul-lib-report": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz",
@@ -9415,9 +9401,9 @@
}
},
"jasmine-core": {
- "version": "2.99.1",
- "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz",
- "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=",
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz",
+ "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==",
"dev": true
},
"jasmine-spec-reporter": {
@@ -9546,18 +9532,17 @@
}
},
"karma": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/karma/-/karma-4.1.0.tgz",
- "integrity": "sha512-xckiDqyNi512U4dXGOOSyLKPwek6X/vUizSy2f3geYevbLj+UIdvNwbn7IwfUIL2g1GXEPWt/87qFD1fBbl/Uw==",
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/karma/-/karma-4.4.1.tgz",
+ "integrity": "sha512-L5SIaXEYqzrh6b1wqYC42tNsFMx2PWuxky84pK9coK09MvmL7mxii3G3bZBh/0rvD27lqDd0le9jyhzvwif73A==",
"dev": true,
"requires": {
"bluebird": "^3.3.0",
"body-parser": "^1.16.1",
- "braces": "^2.3.2",
- "chokidar": "^2.0.3",
+ "braces": "^3.0.2",
+ "chokidar": "^3.0.0",
"colors": "^1.1.0",
"connect": "^3.6.0",
- "core-js": "^2.2.0",
"di": "^0.0.1",
"dom-serialize": "^2.2.0",
"flatted": "^2.0.0",
@@ -9565,7 +9550,7 @@
"graceful-fs": "^4.1.2",
"http-proxy": "^1.13.0",
"isbinaryfile": "^3.0.0",
- "lodash": "^4.17.11",
+ "lodash": "^4.17.14",
"log4js": "^4.0.0",
"mime": "^2.3.1",
"minimatch": "^3.0.2",
@@ -9580,17 +9565,122 @@
"useragent": "2.3.0"
},
"dependencies": {
+ "anymatch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+ "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "binary-extensions": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
+ "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
+ "dev": true
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "chokidar": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz",
+ "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.1",
+ "braces": "~3.0.2",
+ "fsevents": "~2.1.2",
+ "glob-parent": "~5.1.0",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.3.0"
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "fsevents": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
+ "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
+ "dev": true,
+ "optional": true
+ },
+ "glob-parent": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
+ "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
"mime": {
- "version": "2.4.3",
- "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.3.tgz",
- "integrity": "sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw==",
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
+ "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==",
+ "dev": true
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true
},
+ "readdirp": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz",
+ "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.0.7"
+ }
+ },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
}
}
},
@@ -9614,20 +9704,28 @@
}
},
"karma-jasmine": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.2.tgz",
- "integrity": "sha1-OU8rJf+0pkS5rabyLUQ+L9CIhsM=",
- "dev": true
- },
- "karma-jasmine-html-reporter": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz",
- "integrity": "sha1-SKjl7xiAdhfuK14zwRlMNbQ5Ukw=",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-3.1.1.tgz",
+ "integrity": "sha512-pxBmv5K7IkBRLsFSTOpgiK/HzicQT3mfFF+oHAC7nxMfYKhaYFgxOa5qjnHW4sL5rUnmdkSajoudOnnOdPyW4Q==",
"dev": true,
"requires": {
- "karma-jasmine": "^1.0.2"
+ "jasmine-core": "^3.5.0"
+ },
+ "dependencies": {
+ "jasmine-core": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz",
+ "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==",
+ "dev": true
+ }
}
},
+ "karma-jasmine-html-reporter": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.2.tgz",
+ "integrity": "sha512-ILBPsXqQ3eomq+oaQsM311/jxsypw5/d0LnZXj26XkfThwq7jZ55A2CFSKJVA5VekbbOGvMyv7d3juZj0SeTxA==",
+ "dev": true
+ },
"karma-source-map-support": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz",
@@ -9795,16 +9893,16 @@
}
},
"log4js": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.2.0.tgz",
- "integrity": "sha512-1dJ2ORJcdqbzxvzKM2ceqPBh4O6bbICJpB4dvSEUoMcb14s8MqQ/54zNPqekuN5yjGtxO3GUDTvZfQOQhwdqnA==",
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.5.1.tgz",
+ "integrity": "sha512-EEEgFcE9bLgaYUKuozyFfytQM2wDHtXn4tAN41pkaxpNjAykv11GVdeI4tHtmPWW4Xrgh9R/2d7XYghDVjbKKw==",
"dev": true,
"requires": {
"date-format": "^2.0.0",
"debug": "^4.1.1",
"flatted": "^2.0.0",
- "rfdc": "^1.1.2",
- "streamroller": "^1.0.5"
+ "rfdc": "^1.1.4",
+ "streamroller": "^1.0.6"
},
"dependencies": {
"debug": {
@@ -9817,9 +9915,9 @@
}
},
"ms": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
- "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
}
}
@@ -12776,9 +12874,9 @@
}
},
"socket.io-adapter": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz",
- "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
+ "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==",
"dev": true
},
"socket.io-client": {
@@ -13207,16 +13305,16 @@
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
},
"streamroller": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.5.tgz",
- "integrity": "sha512-iGVaMcyF5PcUY0cPbW3xFQUXnr9O4RZXNBBjhuLZgrjLO4XCLLGfx4T2sGqygSeylUjwgWRsnNbT9aV0Zb8AYw==",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.6.tgz",
+ "integrity": "sha512-3QC47Mhv3/aZNFpDDVO44qQb9gwB9QggMEE0sQmkTAwBVYdBRWISdsywlkfm5II1Q5y/pmrHflti/IgmIzdDBg==",
"dev": true,
"requires": {
"async": "^2.6.2",
"date-format": "^2.0.0",
"debug": "^3.2.6",
"fs-extra": "^7.0.1",
- "lodash": "^4.17.11"
+ "lodash": "^4.17.14"
},
"dependencies": {
"debug": {
@@ -13229,9 +13327,9 @@
}
},
"ms": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
- "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
}
}
diff --git a/package.json b/package.json
index 48c5b88..7b0dac3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "mc_frontend",
- "version": "1.7.0",
+ "version": "1.7.2",
"author": "Ionic Framework",
"homepage": "https://ionicframework.com/",
"scripts": {
@@ -9,6 +9,7 @@
"build": "ng build",
"test": "ng test --code-coverage --watch=false",
"test-debug": "ng test --watch=true --browsers=Chrome",
+ "test-cov": "ng test --code-coverage --watch=true",
"lint": "ng lint",
"e2e": "ng e2e"
},
@@ -37,7 +38,7 @@
"cordova-plugin-splashscreen": "^5.0.3",
"cordova-plugin-statusbar": "^2.4.3",
"cordova-plugin-whitelist": "^1.3.4",
- "core-js": "^2.6.11",
+ "core-js": "^3.6.4",
"rxjs": "^6.5.4",
"tslib": "^1.11.1",
"webpack": "^4.42.0",
@@ -53,17 +54,17 @@
"@angular/compiler-cli": "^9.0.4",
"@angular/language-service": "^7.2.16",
"@ionic/angular-toolkit": "^2.2.0",
- "@types/jasmine": "~2.8.8",
+ "@types/jasmine": "^3.5.9",
"@types/jasminewd2": "^2.0.8",
"@types/node": "^12.0.12",
"codelyzer": "^5.2.1",
- "jasmine-core": "~2.99.1",
+ "jasmine-core": "^3.5.0",
"jasmine-spec-reporter": "~4.2.1",
- "karma": "~4.1.0",
+ "karma": "^4.4.1",
"karma-chrome-launcher": "^3.1.0",
"karma-coverage-istanbul-reporter": "^2.0.6",
- "karma-jasmine": "~1.1.2",
- "karma-jasmine-html-reporter": "^0.2.2",
+ "karma-jasmine": "^3.1.1",
+ "karma-jasmine-html-reporter": "^1.5.2",
"protractor": "^5.4.3",
"ts-node": "^8.1.1",
"tslint": "~5.16.0",
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index acaeaf9..1458846 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -22,6 +22,10 @@ const routes: Routes = [
{ path: 'doc-voc-unit', loadChildren: './doc-voc-unit/doc-voc-unit.module#DocVocUnitPageModule' },
{ path: 'doc-exercises', loadChildren: './doc-exercises/doc-exercises.module#DocExercisesPageModule' },
{ path: 'doc-software', loadChildren: './doc-software/doc-software.module#DocSoftwarePageModule' },
+ {
+ path: 'semantics',
+ loadChildren: () => import('./semantics/semantics.module').then( m => m.SemanticsPageModule)
+ },
diff --git a/src/app/app.component.html b/src/app/app.component.html
index bb13d3a..cdc2819 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -14,32 +14,39 @@
-
+
{{'HOME' | translate}}
-
+
{{ 'EXERCISE_GENERATE' | translate }}
-
+
{{ 'EXERCISE_LIST' | translate }}
-
+
{{ 'TEST' | translate }}
+
+
+
+ {{ 'SEMANTICS' | translate }}
+
+
+
@@ -47,35 +54,35 @@
-
+
{{ 'ABOUT' | translate }}
-
+
{{ 'DOC_SOFTWARE' | translate }}
-
+
{{ 'DOC_EXERCISES' | translate }}
-
+
{{ 'DOC_VOC_UNIT' | translate }}
-
+
{{ 'SOURCES' | translate }}
diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts
index 0de89b1..48459e8 100644
--- a/src/app/app.component.spec.ts
+++ b/src/app/app.component.spec.ts
@@ -1,5 +1,5 @@
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
-import {TestBed, async} from '@angular/core/testing';
+import {TestBed, async, ComponentFixture} from '@angular/core/testing';
import {MenuController, Platform} from '@ionic/angular';
import {SplashScreen} from '@ionic-native/splash-screen/ngx';
@@ -13,9 +13,14 @@ import {TranslateTestingModule} from './translate-testing/translate-testing.modu
import {APP_BASE_HREF} from '@angular/common';
import {Subscription} from 'rxjs';
import {HelperService} from './helper.service';
-import MockMC from './models/mock';
+import {CorpusService} from './corpus.service';
+import Spy = jasmine.Spy;
+import MockMC from './models/mockMC';
describe('AppComponent', () => {
+ let statusBarSpy, splashScreenSpy, platformReadySpy, fixture: ComponentFixture,
+ appComponent: AppComponent;
+
class PlatformStub {
backButton = {
subscribeWithPriority(priority: number, callback: () => (Promise | void)): Subscription {
@@ -30,7 +35,6 @@ describe('AppComponent', () => {
}
}
- let statusBarSpy, splashScreenSpy, platformReadySpy, fixture;
beforeEach(async(() => {
platformReadySpy = Promise.resolve();
statusBarSpy = jasmine.createSpyObj('StatusBar', ['styleDefault']);
@@ -49,11 +53,16 @@ describe('AppComponent', () => {
{provide: SplashScreen, useValue: splashScreenSpy},
{provide: Platform, useClass: PlatformStub},
{provide: APP_BASE_HREF, useValue: '/'},
- {provide: MenuController}
+ {provide: MenuController},
+ {provide: CorpusService, useValue: {initCorpusService: () => Promise.resolve()}},
+ {
+ provide: HelperService,
+ useValue: {makeGetRequest: () => Promise.resolve(MockMC.apiResponseCorporaGet)}
+ }
],
- }).compileComponents();
- spyOn(HelperService, 'makeGetRequest').and.returnValue(Promise.resolve(MockMC.apiResponseCorporaGet));
+ }).compileComponents().then();
fixture = TestBed.createComponent(AppComponent);
+ appComponent = fixture.componentInstance;
}));
it('should create the app', () => {
@@ -69,6 +78,20 @@ describe('AppComponent', () => {
expect(splashScreenSpy.hide).toHaveBeenCalled();
});
+ it('should close the menu', () => {
+ const closeSpy: Spy = spyOn(appComponent.menuCtrl, 'close').and.returnValue(Promise.resolve(true));
+ appComponent.closeMenu(true);
+ expect(closeSpy).toHaveBeenCalledTimes(1);
+ });
+
+ it('should initialize the translations', () => {
+ spyOn(appComponent.translate, 'getBrowserLang').and.returnValue(undefined);
+ const languageSpy: Spy = spyOn(appComponent.translate, 'getDefaultLang').and.returnValue('de');
+ appComponent.initTranslate();
+ expect(appComponent.translate.currentLang).toBe('de');
+ expect(languageSpy).toHaveBeenCalledTimes(1);
+ });
+
// TODO: add more tests!
});
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 86696ff..8a667b4 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -13,7 +13,6 @@ import {CorpusService} from './corpus.service';
})
export class AppComponent {
public configMC = configMC;
- public HelperService = HelperService;
constructor(platform: Platform,
public statusBar: StatusBar,
diff --git a/src/app/author-detail/author-detail.page.html b/src/app/author-detail/author-detail.page.html
index 2d35e19..e0abc7a 100644
--- a/src/app/author-detail/author-detail.page.html
+++ b/src/app/author-detail/author-detail.page.html
@@ -2,13 +2,13 @@
-
- {{ state.currentSetup.currentAuthor?.name }}
+
+ {{ state.currentSetup.currentAuthor?.name }}
@@ -19,7 +19,7 @@
-
+
{{corpus.title}}
diff --git a/src/app/author-detail/author-detail.page.spec.ts b/src/app/author-detail/author-detail.page.spec.ts
index df64acd..773dd87 100644
--- a/src/app/author-detail/author-detail.page.spec.ts
+++ b/src/app/author-detail/author-detail.page.spec.ts
@@ -7,9 +7,11 @@ import {IonicStorageModule} from '@ionic/storage';
import {TranslateTestingModule} from '../translate-testing/translate-testing.module';
import {APP_BASE_HREF} from '@angular/common';
import {HttpClientTestingModule} from '@angular/common/http/testing';
+import Spy = jasmine.Spy;
+import {CorpusMC} from '../models/corpusMC';
describe('AuthorDetailPage', () => {
- let component: AuthorDetailPage;
+ let authorDetailPage: AuthorDetailPage;
let fixture: ComponentFixture;
beforeEach(async(() => {
@@ -31,11 +33,19 @@ describe('AuthorDetailPage', () => {
beforeEach(() => {
fixture = TestBed.createComponent(AuthorDetailPage);
- component = fixture.componentInstance;
+ authorDetailPage = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
- expect(component).toBeTruthy();
+ expect(authorDetailPage).toBeTruthy();
+ });
+
+ it('should show possible references', () => {
+ const currentCorpusSpy: Spy = spyOn(authorDetailPage.corpusService, 'setCurrentCorpus');
+ const textRangeSpy: Spy = spyOn(authorDetailPage.helperService, 'goToTextRangePage').and.returnValue(Promise.resolve(true));
+ authorDetailPage.showPossibleReferences(new CorpusMC());
+ expect(currentCorpusSpy).toHaveBeenCalledTimes(1);
+ expect(textRangeSpy).toHaveBeenCalledTimes(1);
});
});
diff --git a/src/app/author-detail/author-detail.page.ts b/src/app/author-detail/author-detail.page.ts
index 4b72136..901610c 100644
--- a/src/app/author-detail/author-detail.page.ts
+++ b/src/app/author-detail/author-detail.page.ts
@@ -12,16 +12,16 @@ import {HttpClient} from '@angular/common/http';
styleUrls: ['./author-detail.page.scss'],
})
export class AuthorDetailPage {
- HelperService = HelperService;
constructor(public navCtrl: NavController,
public corpusService: CorpusService,
public translate: TranslateService,
- public http: HttpClient) {
+ public http: HttpClient,
+ public helperService: HelperService) {
}
showPossibleReferences(corpus: CorpusMC) {
- this.corpusService.saveNewCorpus(corpus);
- HelperService.goToTextRangePage(this.navCtrl).then();
+ this.corpusService.setCurrentCorpus(corpus);
+ this.helperService.goToTextRangePage(this.navCtrl).then();
}
}
diff --git a/src/app/author/author.page.html b/src/app/author/author.page.html
index 9c184d0..7540ad5 100644
--- a/src/app/author/author.page.html
+++ b/src/app/author/author.page.html
@@ -2,12 +2,12 @@
-
+
{{ 'AUTHOR_SELECT' | translate }}
@@ -25,7 +25,7 @@
-
+
diff --git a/src/app/author/author.page.spec.ts b/src/app/author/author.page.spec.ts
index 7e58d11..bae1255 100644
--- a/src/app/author/author.page.spec.ts
+++ b/src/app/author/author.page.spec.ts
@@ -8,9 +8,13 @@ import {HttpClientModule} from '@angular/common/http';
import {IonicStorageModule} from '@ionic/storage';
import {TranslateTestingModule} from '../translate-testing/translate-testing.module';
import {APP_BASE_HREF} from '@angular/common';
+import {Author} from '../models/author';
+import {CorpusMC} from '../models/corpusMC';
+import Spy = jasmine.Spy;
+import MockMC from '../models/mockMC';
describe('AuthorPage', () => {
- let component: AuthorPage;
+ let authorPage: AuthorPage;
let fixture: ComponentFixture;
beforeEach(async(() => {
@@ -35,11 +39,66 @@ describe('AuthorPage', () => {
beforeEach(() => {
fixture = TestBed.createComponent(AuthorPage);
- component = fixture.componentInstance;
+ authorPage = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
- expect(component).toBeTruthy();
+ expect(authorPage).toBeTruthy();
+ });
+
+ it('should filter an author', () => {
+ const result: boolean = AuthorPage.filterAuthor(new Author({name: 'name'}), 'b');
+ expect(result).toBe(false);
+ });
+
+ it('should get authors', () => {
+ authorPage.showOnlyTreebanks = true;
+ authorPage.getAuthors('');
+ expect(authorPage.authorsDisplayed).toBe(authorPage.baseAuthorList);
+ authorPage.showOnlyTreebanks = false;
+ authorPage.getAuthors('');
+ expect(authorPage.authorsDisplayed).toBe(authorPage.corpusService.availableAuthors);
+ authorPage.baseAuthorList.push(new Author({name: 'test'}));
+ authorPage.getAuthors('test');
+ expect(authorPage.authorsDisplayed.length).toBe(1);
+ });
+
+ it('should be initialized', () => {
+ authorPage.corpusService.availableAuthors = [new Author({
+ corpora: [new CorpusMC({source_urn: 'proiel'})],
+ name: 'name'
+ })];
+ authorPage.ngOnInit();
+ expect(authorPage.baseAuthorList.length).toBe(1);
+ });
+
+ it('should restore the last setup', (done) => {
+ const restoreSpy: Spy = spyOn(authorPage.corpusService, 'restoreLastCorpus').and.callFake(() => Promise.reject());
+ const showTextSpy: Spy = spyOn(authorPage.helperService, 'goToShowTextPage').and.returnValue(Promise.resolve(true));
+ const vocCheckSpy: Spy = spyOn(authorPage.helperService, 'goToVocabularyCheckPage').and.returnValue(Promise.resolve(true));
+ authorPage.helperService.isVocabularyCheck = false;
+ authorPage.restoreLastSetup().then(() => {
+ }, () => {
+ expect(showTextSpy).toHaveBeenCalledTimes(0);
+ restoreSpy.and.returnValue(Promise.resolve());
+ authorPage.restoreLastSetup().then(() => {
+ expect(showTextSpy).toHaveBeenCalledTimes(1);
+ authorPage.helperService.isVocabularyCheck = true;
+ authorPage.restoreLastSetup().then(() => {
+ expect(vocCheckSpy).toHaveBeenCalledTimes(1);
+ const a = 0;
+ done();
+ });
+ });
+ });
+ });
+
+ it('should show corpora', () => {
+ authorPage.helperService.applicationState.next(authorPage.helperService.deepCopy(MockMC.applicationState));
+ spyOn(authorPage.helperService, 'goToAuthorDetailPage').and.returnValue(Promise.resolve(true));
+ const author: Author = new Author({name: '', corpora: []});
+ authorPage.showCorpora(author);
+ expect(authorPage.corpusService.currentAuthor).toBe(author);
});
});
diff --git a/src/app/author/author.page.ts b/src/app/author/author.page.ts
index d330184..704739e 100644
--- a/src/app/author/author.page.ts
+++ b/src/app/author/author.page.ts
@@ -1,4 +1,4 @@
-import {Component} from '@angular/core';
+import {Component, OnInit} from '@angular/core';
import {Author} from 'src/app/models/author';
import {NavController} from '@ionic/angular';
import {TranslateService} from '@ngx-translate/core';
@@ -21,34 +21,25 @@ import {take} from 'rxjs/operators';
templateUrl: './author.page.html',
styleUrls: ['./author.page.scss'],
})
-export class AuthorPage {
-
+export class AuthorPage implements OnInit {
constructor(public navCtrl: NavController,
public translate: TranslateService,
public corpusService: CorpusService,
public http: HttpClient,
public exerciseService: ExerciseService,
public helperService: HelperService) {
- if (!this.corpusService.availableAuthors.length) {
- this.corpusService.loadCorporaFromLocalStorage().then(() => {
- this.toggleTreebankAuthors();
- });
- } else {
- this.toggleTreebankAuthors();
- }
}
public authorsDisplayed: Author[];
public baseAuthorList: Author[];
- HelperService = HelperService;
showOnlyTreebanks = true;
currentSearchValue = '';
- static filterAuthor(author: Author, filterValue: string) {
+ static filterAuthor(author: Author, filterValue: string): boolean {
return author.name.toLowerCase().includes(filterValue.toLowerCase());
}
- getAuthors(newSearchValue: string) {
+ getAuthors(newSearchValue: string): void {
this.baseAuthorList = this.showOnlyTreebanks ? this.getAuthorsFiltered() : this.corpusService.availableAuthors;
if (!newSearchValue) {
this.authorsDisplayed = this.baseAuthorList;
@@ -59,31 +50,46 @@ export class AuthorPage {
}
}
- getAuthorsFiltered() {
+ getAuthorsFiltered(): Author[] {
return this.corpusService.availableAuthors.filter(author => author.corpora.some(corpus => this.corpusService.isTreebank(corpus)));
}
- restoreLastSetup() {
- this.corpusService.restoreLastCorpus().then(() => {
- if (HelperService.isVocabularyCheck) {
- HelperService.goToVocabularyCheckPage(this.navCtrl).then();
- } else {
- HelperService.goToShowTextPage(this.navCtrl).then();
- }
- }, () => {
+ ngOnInit(): void {
+ if (!this.corpusService.availableAuthors.length) {
+ this.corpusService.loadCorporaFromLocalStorage().then(() => {
+ this.toggleTreebankAuthors();
+ });
+ } else {
+ this.toggleTreebankAuthors();
+ }
+ }
+
+ restoreLastSetup(): Promise {
+ return new Promise((resolve, reject) => {
+ this.corpusService.restoreLastCorpus().then(() => {
+ if (this.helperService.isVocabularyCheck) {
+ this.helperService.goToVocabularyCheckPage(this.navCtrl).then();
+ return resolve();
+ } else {
+ this.helperService.goToShowTextPage(this.navCtrl).then();
+ return resolve();
+ }
+ }, () => {
+ return reject();
+ });
});
}
- showCorpora(author: Author) {
+ showCorpora(author: Author): void {
this.corpusService.currentAuthor = author;
- HelperService.applicationState.pipe(take(1)).subscribe((as: ApplicationState) => {
+ this.helperService.applicationState.pipe(take(1)).subscribe((as: ApplicationState) => {
as.currentSetup.currentAuthor = author;
this.helperService.saveApplicationState(as).then();
- this.navCtrl.navigateForward('/author-detail').then();
+ this.helperService.goToAuthorDetailPage(this.navCtrl).then();
});
}
- toggleTreebankAuthors() {
+ toggleTreebankAuthors(): void {
this.baseAuthorList = this.showOnlyTreebanks ? this.getAuthorsFiltered() : this.corpusService.availableAuthors;
this.authorsDisplayed = this.baseAuthorList.filter((author: Author) => {
return AuthorPage.filterAuthor(author, this.currentSearchValue);
diff --git a/src/app/confirm-cancel/confirm-cancel.page.spec.ts b/src/app/confirm-cancel/confirm-cancel.page.spec.ts
index 9a34e64..3294ac2 100644
--- a/src/app/confirm-cancel/confirm-cancel.page.spec.ts
+++ b/src/app/confirm-cancel/confirm-cancel.page.spec.ts
@@ -9,7 +9,7 @@ import {TranslateTestingModule} from '../translate-testing/translate-testing.mod
import {APP_BASE_HREF} from '@angular/common';
describe('ConfirmCancelPage', () => {
- let component: ConfirmCancelPage;
+ let confirmCancelPage: ConfirmCancelPage;
let fixture: ComponentFixture;
beforeEach(async(() => {
@@ -26,16 +26,23 @@ describe('ConfirmCancelPage', () => {
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
- .compileComponents();
+ .compileComponents().then();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ConfirmCancelPage);
- component = fixture.componentInstance;
+ confirmCancelPage = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
- expect(component).toBeTruthy();
+ expect(confirmCancelPage).toBeTruthy();
+ });
+
+ it('should confirm', () => {
+ confirmCancelPage.helperService.currentPopover = {dismiss: () => Promise.resolve(true)} as HTMLIonPopoverElement;
+ spyOn(confirmCancelPage.helperService, 'goToHomePage').and.returnValue(Promise.resolve(true));
+ confirmCancelPage.confirm();
+ expect(confirmCancelPage.helperService.currentPopover).toBeFalsy();
});
});
diff --git a/src/app/confirm-cancel/confirm-cancel.page.ts b/src/app/confirm-cancel/confirm-cancel.page.ts
index 29c6f25..229195e 100644
--- a/src/app/confirm-cancel/confirm-cancel.page.ts
+++ b/src/app/confirm-cancel/confirm-cancel.page.ts
@@ -1,27 +1,25 @@
-import {Component, OnInit} from '@angular/core';
+import {Component} from '@angular/core';
import {HelperService} from '../helper.service';
-import {NavController} from "@ionic/angular";
+import {NavController} from '@ionic/angular';
@Component({
selector: 'app-confirm-cancel',
templateUrl: './confirm-cancel.page.html',
styleUrls: ['./confirm-cancel.page.scss'],
})
-export class ConfirmCancelPage implements OnInit {
+export class ConfirmCancelPage {
- constructor(public navCtrl: NavController) {
+ constructor(public navCtrl: NavController,
+ public helperService: HelperService) {
}
- ngOnInit() {
+ exit(): void {
+ this.helperService.currentPopover.dismiss().then();
+ this.helperService.currentPopover = null;
}
- exit() {
- HelperService.currentPopover.dismiss().then();
- HelperService.currentPopover = null;
- }
-
- confirm() {
- HelperService.goToHomePage(this.navCtrl).then();
+ confirm(): void {
+ this.helperService.goToHomePage(this.navCtrl).then();
this.exit();
}
}
diff --git a/src/app/corpus.service.spec.ts b/src/app/corpus.service.spec.ts
index 4e0b035..4daddf4 100644
--- a/src/app/corpus.service.spec.ts
+++ b/src/app/corpus.service.spec.ts
@@ -1,4 +1,4 @@
-import {TestBed} from '@angular/core/testing';
+import {fakeAsync, flushMicrotasks, TestBed} from '@angular/core/testing';
import {CorpusService} from './corpus.service';
import {IonicStorageModule} from '@ionic/storage';
@@ -8,12 +8,29 @@ import {APP_BASE_HREF} from '@angular/common';
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
import {HttpClient} from '@angular/common/http';
import {HelperService} from './helper.service';
-import MockMC from './models/mock';
+import MockMC from './models/mockMC';
+import {CaseValue, DependencyValue, ExerciseType, PartOfSpeechValue, Phenomenon} from './models/enum';
+import {AnnisResponse} from './models/annisResponse';
+import {ApplicationState} from './models/applicationState';
+import {QueryMC} from './models/queryMC';
+import {PhenomenonMapContent} from './models/phenomenonMap';
+import {FrequencyItem} from './models/frequencyItem';
+import {UpdateInfo} from './models/updateInfo';
+import configMC from '../configMC';
+import {TextData} from './models/textData';
+import {CorpusMC} from './models/corpusMC';
+import {LinkMC} from './models/linkMC';
+import {NodeMC} from './models/nodeMC';
+import {Author} from './models/author';
+import {take} from 'rxjs/operators';
+import {TextRange} from './models/textRange';
+import Spy = jasmine.Spy;
describe('CorpusService', () => {
let httpClient: HttpClient;
let httpTestingController: HttpTestingController;
let corpusService: CorpusService;
+ let helperService: HelperService;
beforeEach(async () => {
TestBed.configureTestingModule({
imports: [
@@ -24,11 +41,13 @@ describe('CorpusService', () => {
],
providers: [
{provide: APP_BASE_HREF, useValue: '/'},
+ HelperService,
],
});
- httpClient = TestBed.get(HttpClient);
- httpTestingController = TestBed.get(HttpTestingController);
- corpusService = TestBed.get(CorpusService);
+ httpClient = TestBed.inject(HttpClient);
+ httpTestingController = TestBed.inject(HttpTestingController);
+ corpusService = TestBed.inject(CorpusService);
+ helperService = TestBed.inject(HelperService);
});
afterEach(() => {
@@ -38,11 +57,355 @@ describe('CorpusService', () => {
expect(corpusService).toBeTruthy();
});
- it('should load corpora', (done) => {
- spyOn(HelperService, 'makeGetRequest').and.returnValue(Promise.resolve(MockMC.apiResponseCorporaGet));
+ it('should adjust translations', (done) => {
+ spyOn(helperService, 'makeGetRequest').and.returnValue(Promise.resolve(MockMC.apiResponseFrequencyAnalysisGet));
+ spyOn(corpusService, 'getSortedQueryValues').and.returnValue([PartOfSpeechValue.adjective.toString()]);
+ helperService.applicationState.next(helperService.deepCopy(MockMC.applicationState) as ApplicationState);
+ corpusService.exercise.type = ExerciseType.matching;
+ corpusService.annisResponse = new AnnisResponse({frequency_analysis: []});
+ corpusService.adjustTranslations().then(() => {
+ expect(corpusService.exercise.queryItems.length).toBe(2);
+ corpusService.exercise.type = ExerciseType.cloze;
+ corpusService.adjustTranslations().then(() => {
+ expect(corpusService.exercise.queryItems.length).toBe(1);
+ done();
+ });
+ });
+ });
+
+ it('should check for an existing ANNIS response', (done) => {
+ helperService.applicationState.error(0);
+ corpusService.checkAnnisResponse().then(() => {
+ }, () => {
+ expect(corpusService.annisResponse).toBeFalsy();
+ helperService.applicationState.next(new ApplicationState());
+ corpusService.checkAnnisResponse().then(() => {
+ }, () => {
+ expect(corpusService.annisResponse).toBeFalsy();
+ helperService.applicationState.next(helperService.deepCopy(MockMC.applicationState) as ApplicationState);
+ corpusService.checkAnnisResponse().then(() => {
+ expect(corpusService.annisResponse).toBeTruthy();
+ corpusService.checkAnnisResponse().then(() => {
+ expect(corpusService.annisResponse).toBeTruthy();
+ done();
+ }, () => {
+ });
+ }, () => {
+ });
+ });
+ });
+ });
+
+ it('should check for updates', (done) => {
+ const updateInfoSpy: Spy = spyOn(corpusService.storage, 'get').withArgs(configMC.localStorageKeyUpdateInfo)
+ .and.returnValue(Promise.resolve(JSON.stringify(null)));
+ const getCorporaSpy: Spy = spyOn(corpusService, 'getCorpora').and.callFake(() => Promise.reject());
+ corpusService.checkForUpdates().then(() => {
+ }, () => {
+ expect(getCorporaSpy).toHaveBeenCalledTimes(1);
+ expect(corpusService.availableCorpora).toBeFalsy();
+ updateInfoSpy.and.returnValue(Promise.resolve(JSON.stringify(new UpdateInfo({corpora: 0}))));
+ getCorporaSpy.and.returnValue(Promise.resolve());
+ corpusService.checkForUpdates().then(() => {
+ expect(getCorporaSpy).toHaveBeenCalledTimes(2);
+ expect(corpusService.availableCorpora).toBeFalsy();
+ done();
+ });
+ });
+ });
+
+ it('should get corpora', (done) => {
+ corpusService.availableCorpora = [];
+ const requestSpy: Spy = spyOn(helperService, 'makeGetRequest').and.returnValue(Promise.resolve(null));
+ const localStorageSpy: Spy = spyOn(corpusService, 'loadCorporaFromLocalStorage').and.returnValue(Promise.resolve());
+ corpusService.getCorpora().then(() => {
+ expect(localStorageSpy).toHaveBeenCalledTimes(1);
+ expect(corpusService.availableCorpora.length).toBe(0);
+ requestSpy.and.callFake(() => Promise.reject());
+ corpusService.getCorpora().then(() => {
+ }, () => {
+ expect(localStorageSpy).toHaveBeenCalledTimes(2);
+ expect(corpusService.availableCorpora.length).toBe(0);
+ requestSpy.and.returnValue(Promise.resolve(MockMC.apiResponseCorporaGet));
+ corpusService.getCorpora().then(() => {
+ expect(corpusService.availableCorpora.length).toBe(1);
+ done();
+ });
+ });
+ });
+ });
+
+ it('should get CTS text passage', (done) => {
+ const spy: Spy = spyOn(helperService, 'makeGetRequest').and.returnValue(Promise.reject(1));
+ corpusService.getCTStextPassage('').then(() => {
+ }, (error: number) => {
+ expect(error).toBe(1);
+ spy.and.returnValue(Promise.resolve(
+ (helperService.deepCopy(MockMC.applicationState) as ApplicationState).mostRecentSetup.annisResponse));
+ corpusService.getCTStextPassage('').then((ar: AnnisResponse) => {
+ expect(ar.nodes.length).toBe(1);
+ done();
+ });
+ });
+ });
+
+ it('should get CTS valid reff', (done) => {
+ const spy: Spy = spyOn(helperService, 'makeGetRequest').and.returnValue(Promise.reject(1));
+ corpusService.getCTSvalidReff('').then(() => {
+ }, (error: number) => {
+ expect(error).toBe(1);
+ spy.and.returnValue(Promise.resolve(['']));
+ corpusService.getCTSvalidReff('').then((reff: string[]) => {
+ expect(reff.length).toBe(1);
+ done();
+ });
+ });
+ });
+
+ it('should get a frequency analysis', (done) => {
+ const spy: Spy = spyOn(helperService, 'makeGetRequest').and.callFake(() => Promise.reject());
+ corpusService.annisResponse = new AnnisResponse({frequency_analysis: []});
+ corpusService.getFrequencyAnalysis().then(() => {
+ }, () => {
+ expect(spy).toHaveBeenCalledTimes(1);
+ spy.and.returnValue(Promise.resolve(MockMC.apiResponseFrequencyAnalysisGet));
+ corpusService.getFrequencyAnalysis().then(() => {
+ expect(spy).toHaveBeenCalledTimes(2);
+ corpusService.getFrequencyAnalysis().then(() => {
+ expect(corpusService.annisResponse.frequency_analysis.length).toBe(1);
+ done();
+ });
+ });
+ });
+ });
+
+ it('should get sorted query values', () => {
+ corpusService.exercise.type = ExerciseType.cloze;
+ const query: QueryMC = new QueryMC({phenomenon: Phenomenon.partOfSpeech});
+ let result: string[] = corpusService.getSortedQueryValues(query, 0);
+ expect(result.length).toBe(0);
+ const pmc: PhenomenonMapContent = corpusService.phenomenonMap[query.phenomenon];
+ pmc.specificValues = {a: 0, b: 1, c: 2};
+ pmc.translationValues = {a: 'a', b: 'c', c: 'b'};
+ result = corpusService.getSortedQueryValues(query, 0);
+ expect(result.length).toBe(3);
+ corpusService.exercise.type = ExerciseType.matching;
+ corpusService.annisResponse = new AnnisResponse({
+ frequency_analysis: [new FrequencyItem({
+ values: [PartOfSpeechValue.adjective.toString(), 'a'],
+ phenomena: ['', Phenomenon.partOfSpeech.toString()]
+ }), new FrequencyItem({
+ values: [PartOfSpeechValue.adjective.toString(), 'b'],
+ phenomena: ['', Phenomenon.partOfSpeech.toString()]
+ }), new FrequencyItem({
+ values: [PartOfSpeechValue.adjective.toString(), 'c'],
+ phenomena: ['', Phenomenon.partOfSpeech.toString()]
+ })]
+ });
+ result = corpusService.getSortedQueryValues(query, 1);
+ expect(result.length).toBe(3);
+ corpusService.annisResponse.frequency_analysis.forEach(fi => fi.phenomena = [Phenomenon.partOfSpeech.toString()]);
+ corpusService.annisResponse.frequency_analysis[0].values[0] = 'a';
+ corpusService.annisResponse.frequency_analysis[1].values[0] = 'b';
+ corpusService.annisResponse.frequency_analysis[2].values[0] = 'c';
+ result = corpusService.getSortedQueryValues(query, 0);
+ expect(result.length).toBe(3);
+ });
+
+ it('should get text', (done) => {
+ helperService.isVocabularyCheck = true;
+ corpusService.getText().then(() => {
+ expect(corpusService.currentText).toBeFalsy();
+ helperService.isVocabularyCheck = false;
+ const spy: Spy = spyOn(corpusService, 'getCTStextPassage').and.callFake(() => Promise.reject());
+ corpusService.getText().then(() => {
+ }, () => {
+ expect(corpusService.currentText).toBeFalsy();
+ spy.and.returnValue(Promise.resolve(MockMC.apiResponseTextGet));
+ corpusService.getText().then(() => {
+ expect(corpusService.currentText).toBeTruthy();
+ done();
+ });
+ });
+ });
+ });
+
+ it('should initialize the corpus service', (done) => {
+ const restoreSpy: Spy = spyOn(corpusService, 'restoreLastCorpus').and.returnValue(Promise.resolve());
+ const updateInfoSpy: Spy = spyOn(corpusService, 'initUpdateInfo').and.callFake(() => Promise.reject());
corpusService.initCorpusService().then(() => {
- expect(HelperService.makeGetRequest).toHaveBeenCalled();
+ }, () => {
+ corpusService.translate.onLangChange.next();
+ expect(restoreSpy).toHaveBeenCalledTimes(0);
+ updateInfoSpy.and.returnValue(Promise.resolve());
+ const getCorporaSpy: Spy = spyOn(corpusService, 'getCorpora').and.returnValue(Promise.resolve());
+ corpusService.initCorpusService().then(() => {
+ expect(getCorporaSpy).toHaveBeenCalledTimes(1);
+ expect(restoreSpy).toHaveBeenCalledTimes(1);
+ done();
+ });
+ });
+ });
+
+ it('should initialize the current corpus', fakeAsync(() => {
+ helperService.applicationState.next(new ApplicationState());
+ let corpus: CorpusMC;
+
+ function initCorpus(): void {
+ corpusService.initCurrentCorpus().then(() => {
+ corpusService.currentCorpus.subscribe((cc: CorpusMC) => {
+ corpus = cc;
+ });
+ });
+ flushMicrotasks();
+ }
+
+ initCorpus();
+ expect(corpus).toBeUndefined();
+ helperService.applicationState.next(helperService.deepCopy(MockMC.applicationState) as ApplicationState);
+ initCorpus();
+ expect(corpus).toBeTruthy();
+ corpus = undefined;
+ initCorpus();
+ expect(corpus).toBeTruthy();
+ }));
+
+ it('should initialize the update information', (done) => {
+ corpusService.storage.set(configMC.localStorageKeyUpdateInfo, null).then(() => {
+ corpusService.initUpdateInfo().then(() => {
+ corpusService.storage.get(configMC.localStorageKeyUpdateInfo).then((jsonString: string) => {
+ expect(jsonString).toBeTruthy();
+ const setSpy: Spy = spyOn(corpusService.storage, 'set').and.returnValue(Promise.resolve());
+ corpusService.initUpdateInfo().then(() => {
+ expect(setSpy).toHaveBeenCalledTimes(0);
+ done();
+ });
+ });
+ });
+ });
+ });
+
+ it('should load corpora from local storage', (done) => {
+ corpusService.availableCorpora = [];
+ spyOn(corpusService.storage, 'get').withArgs(configMC.localStorageKeyCorpora).and.returnValue(
+ Promise.resolve(JSON.stringify([new CorpusMC({source_urn: 'urn'})])));
+ corpusService.loadCorporaFromLocalStorage().then(() => {
+ expect(corpusService.availableCorpora.length).toBe(1);
done();
});
});
+
+ it('should process an ANNIS response', () => {
+ const ar: AnnisResponse = new AnnisResponse({
+ links: [new LinkMC({annis_component_type: 'Pointing', udep_deprel: 'nsubj'})],
+ nodes: [new NodeMC({annis_tok: 'tok .'})]
+ });
+ corpusService.processAnnisResponse(ar);
+ expect(corpusService.phenomenonMap.dependency.specificValues[DependencyValue.subject]).toBe(1);
+ ar.links.push(ar.links[0]);
+ corpusService.processAnnisResponse(ar);
+ expect(corpusService.phenomenonMap.dependency.specificValues[DependencyValue.subject]).toBe(2);
+ });
+
+ it('should process corpora', () => {
+ const corpusList: CorpusMC[] = [
+ new CorpusMC({author: '', source_urn: 'proiel'}),
+ new CorpusMC({author: 'b', source_urn: 'b', title: 't1'}),
+ new CorpusMC({author: 'b', source_urn: 'b', title: 't1'}),
+ new CorpusMC({author: 'b', source_urn: 'c', title: 't3'}),
+ new CorpusMC({author: 'b', source_urn: 'e', title: 't2'}),
+ new CorpusMC({author: 'a', source_urn: 'd'}),
+ ];
+ corpusService.availableAuthors = [new Author({name: ' PROIEL', corpora: []})];
+ corpusService.processCorpora(corpusList);
+ expect(corpusList[0].author.length).toBeGreaterThan(0);
+ expect(corpusService.availableAuthors[0].corpora.length).toBe(1);
+ });
+
+ it('should process nodes', () => {
+ const node: NodeMC = new NodeMC({
+ udep_lemma: 'lemma',
+ udep_upostag: 'NOUN',
+ udep_feats: `${Phenomenon.case.toString()}=Nom`
+ });
+ const ar: AnnisResponse = new AnnisResponse({nodes: [node, new NodeMC({...node})]});
+ corpusService.phenomenonMap.lemma = new PhenomenonMapContent({specificValues: {}, translationValues: {}});
+ corpusService.phenomenonMap.case = new PhenomenonMapContent({specificValues: {}, translationValues: {}});
+ corpusService.phenomenonMap.partOfSpeech = new PhenomenonMapContent({
+ specificValues: {},
+ translationValues: {}
+ });
+ corpusService.processNodes(ar);
+ expect(corpusService.phenomenonMap.lemma.specificValues[node.udep_lemma]).toBe(2);
+ expect(corpusService.phenomenonMap.case.specificValues[CaseValue.nominative]).toBe(2);
+ expect(corpusService.phenomenonMap.partOfSpeech.specificValues[PartOfSpeechValue.noun]).toBe(2);
+ });
+
+ it('should restore the last corpus', (done) => {
+ helperService.applicationState.next(new ApplicationState({mostRecentSetup: new TextData()}));
+ corpusService.currentText = '';
+ const getTextSpy: Spy = spyOn(corpusService, 'getText').and.callFake(() => Promise.reject());
+ corpusService.restoreLastCorpus().then(() => {
+ }, () => {
+ expect(getTextSpy).toHaveBeenCalledTimes(1);
+ getTextSpy.and.returnValue(Promise.resolve());
+ helperService.applicationState.next(new ApplicationState({
+ mostRecentSetup: new TextData({
+ annisResponse: new AnnisResponse({nodes: []})
+ })
+ }));
+ corpusService.restoreLastCorpus().then(() => {
+ expect(getTextSpy).toHaveBeenCalledTimes(2);
+ corpusService.currentText = 'text';
+ corpusService.restoreLastCorpus().then(() => {
+ expect(getTextSpy).toHaveBeenCalledTimes(2);
+ helperService.applicationState.next(helperService.deepCopy(MockMC.applicationState) as ApplicationState);
+ corpusService.restoreLastCorpus().then(() => {
+ expect(corpusService.annisResponse.nodes.length).toBe(1);
+ done();
+ });
+ });
+ });
+ });
+ });
+
+ it('should save a new corpus', (done) => {
+ corpusService.initCurrentTextRange();
+ corpusService.initCurrentCorpus().then(() => {
+ corpusService.setCurrentCorpus(new CorpusMC());
+ corpusService.currentCorpus.pipe(take(1)).subscribe((corpus: CorpusMC) => {
+ expect(corpus).toBeTruthy();
+ done();
+ });
+ });
+ });
+
+ it('should set the current text range', () => {
+ corpusService.initCurrentTextRange();
+ const input: HTMLInputElement = document.createElement('input');
+ input.setAttribute('id', 'input1');
+ input.setAttribute('value', '1');
+ document.querySelector('body').appendChild(input);
+ corpusService.setCurrentTextRange(1);
+ corpusService.setCurrentTextRange(4, '2');
+ corpusService.currentTextRange.pipe(take(1)).subscribe((tr: TextRange) => {
+ expect(tr.start[0]).toBe('1');
+ expect(tr.end[0]).toBe('2');
+ });
+ });
+
+ it('should update the base word', () => {
+ const adjustSpy: Spy = spyOn(corpusService, 'adjustQueryValue');
+ const queryValuesSpy: Spy = spyOn(corpusService, 'getSortedQueryValues').and.returnValue([]);
+ corpusService.annisResponse = new AnnisResponse({
+ frequency_analysis: helperService.deepCopy(MockMC.apiResponseFrequencyAnalysisGet)
+ });
+ corpusService.annisResponse.frequency_analysis[0].phenomena.push(Phenomenon.case.toString());
+ corpusService.exercise.type = ExerciseType.matching;
+ corpusService.exercise.queryItems.push(new QueryMC());
+ corpusService.updateBaseWord(new QueryMC(), 0);
+ expect(adjustSpy).toHaveBeenCalledTimes(1);
+ expect(queryValuesSpy).toHaveBeenCalledTimes(1);
+ expect(corpusService.exercise.queryItems[1].phenomenon).toBe(Phenomenon.case);
+ });
});
diff --git a/src/app/corpus.service.ts b/src/app/corpus.service.ts
index 04bf9be..2c49d5f 100644
--- a/src/app/corpus.service.ts
+++ b/src/app/corpus.service.ts
@@ -52,6 +52,9 @@ export class CorpusService {
public currentTextRange: ReplaySubject;
private currentTextRangeCache: TextRange = new TextRange({start: ['', '', ''], end: ['', '', '']});
public currentUrn = '';
+ public dataAlreadySentMessage: string;
+ public dataSentSuccessMessage: string;
+ public emptyQueryValueString: string;
public exercise: Exercise = new Exercise({
type: ExerciseType.cloze,
typeTranslation: '',
@@ -63,7 +66,6 @@ export class CorpusService {
instructionsTranslation: ''
});
public invalidTextRangeString: string;
- public isMostRecentSetupLoaded = false;
public isTextRangeCorrect = false;
public phenomenonMap: PhenomenonMap = new PhenomenonMap({
case: new PhenomenonMapContent({translationObject: CaseTranslations}),
@@ -71,7 +73,10 @@ export class CorpusService {
lemma: new PhenomenonMapContent({translationObject: null}),
partOfSpeech: new PhenomenonMapContent({translationObject: PartOfSpeechTranslation})
});
+ public searchRegexMissingString: string;
public shareLinkCopiedString: string;
+ public textTooLongString: string;
+ public tooManyHitsString: string;
constructor(public translate: TranslateService,
public http: HttpClient,
@@ -81,50 +86,52 @@ export class CorpusService {
) {
}
- adjustQueryValue(query: QueryMC, queryIndex: number) {
+ adjustQueryValue(query: QueryMC, queryIndex: number): void {
// when the phenomenon changes, choose the first value from the translated list as the default
query.values = [this.getSortedQueryValues(query, queryIndex)[0]];
this.updateBaseWord(query, queryIndex);
}
- adjustTranslations() {
- this.translate.get(ExerciseTypeTranslation[this.exercise.type]).subscribe(
- value => this.exercise.typeTranslation = value);
- if ([ExerciseType.cloze, ExerciseType.matching, ExerciseType.markWords].indexOf(this.exercise.type) > -1) {
- this.translate.get(InstructionsTranslation[this.exercise.type]).subscribe(
- value => this.exercise.instructionsTranslation = value);
- }
- if (this.exercise.type === ExerciseType.matching) {
- this.exercise.queryItems = [new QueryMC({phenomenon: Phenomenon.partOfSpeech, values: []}),
- new QueryMC({phenomenon: Phenomenon.partOfSpeech, values: []})];
- this.getFrequencyAnalysis().then(() => {
- this.adjustQueryValue(this.exercise.queryItems[0], 0);
- this.adjustQueryValue(this.exercise.queryItems[1], 1);
- return;
- });
- } else if (this.exercise.queryItems.length > 1) {
- this.exercise.queryItems.splice(1, 1);
- }
- this.adjustQueryValue(this.exercise.queryItems[0], 0);
+ adjustTranslations(): Promise {
+ return new Promise(resolve => {
+ this.translate.get(ExerciseTypeTranslation[this.exercise.type]).subscribe(
+ value => this.exercise.typeTranslation = value);
+ if ([ExerciseType.cloze, ExerciseType.matching, ExerciseType.markWords].indexOf(this.exercise.type) > -1) {
+ this.translate.get(InstructionsTranslation[this.exercise.type]).subscribe(
+ value => this.exercise.instructionsTranslation = value);
+ }
+ if (this.exercise.type === ExerciseType.matching) {
+ this.exercise.queryItems = [new QueryMC({phenomenon: Phenomenon.partOfSpeech, values: []}),
+ new QueryMC({phenomenon: Phenomenon.partOfSpeech, values: []})];
+ this.getFrequencyAnalysis().then(() => {
+ this.adjustQueryValue(this.exercise.queryItems[0], 0);
+ this.adjustQueryValue(this.exercise.queryItems[1], 1);
+ return resolve();
+ });
+ } else if (this.exercise.queryItems.length > 1) {
+ this.exercise.queryItems.splice(1, 1);
+ return resolve();
+ }
+ }).finally(() => this.adjustQueryValue(this.exercise.queryItems[0], 0));
}
- checkAnnisResponse() {
- return new Promise((outerResolve, outerReject) => {
+ checkAnnisResponse(): Promise {
+ return new Promise((resolve, reject) => {
if (this.annisResponse) {
- return outerResolve();
+ return resolve();
}
- HelperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
+ this.helperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
if (state.mostRecentSetup) {
this.annisResponse = state.mostRecentSetup.annisResponse;
this.currentAuthor = state.mostRecentSetup.currentAuthor;
this.currentUrn = state.mostRecentSetup.currentUrn;
this.currentCorpusCache = state.mostRecentSetup.currentCorpus;
- return outerResolve();
+ return resolve();
} else {
- return outerReject();
+ return reject();
}
}, () => {
- return outerReject();
+ return reject();
});
});
}
@@ -151,26 +158,28 @@ export class CorpusService {
// get corpora from REST API
const url: string = configMC.backendBaseUrl + configMC.backendApiCorporaPath;
const params: HttpParams = new HttpParams().set('last_update_time', lastUpdateTimeMS.toString());
- HelperService.makeGetRequest(this.http, this.toastCtrl, url, params, this.corporaUnavailableString).then((data: object) => {
- if (data) {
- const corpusList: CorpusMC[] = data['corpora'] as CorpusMC[];
- this.storage.set(configMC.localStorageKeyCorpora, JSON.stringify(corpusList)).then();
- this.storage.get(configMC.localStorageKeyUpdateInfo).then((jsonString: string) => {
- const updateInfo: UpdateInfo = JSON.parse(jsonString) as UpdateInfo;
- updateInfo.corpora = new Date().getTime();
- this.storage.set(configMC.localStorageKeyUpdateInfo, JSON.stringify(updateInfo)).then();
- });
- this.processCorpora(corpusList);
- return resolve();
- } else {
- this.loadCorporaFromLocalStorage().then(() => {
+ this.helperService.makeGetRequest(this.http, this.toastCtrl, url, params, this.corporaUnavailableString)
+ .then((data: object) => {
+ if (data) {
+ const corpusList: CorpusMC[] = data['corpora'] as CorpusMC[];
+ this.storage.set(configMC.localStorageKeyCorpora, JSON.stringify(corpusList)).then();
+ this.storage.get(configMC.localStorageKeyUpdateInfo).then((jsonString: string) => {
+ const updateInfo: UpdateInfo = JSON.parse(jsonString) as UpdateInfo;
+ updateInfo.corpora = new Date().getTime();
+ this.storage.set(configMC.localStorageKeyUpdateInfo, JSON.stringify(updateInfo)).then();
+ });
+ this.processCorpora(corpusList);
return resolve();
+ } else {
+ this.loadCorporaFromLocalStorage().then(() => {
+ return resolve();
+ });
+ }
+ }, async (error: HttpErrorResponse) => {
+ this.loadCorporaFromLocalStorage().then(() => {
+ return reject();
});
- }
- }, async (error: HttpErrorResponse) => {
- this.loadCorporaFromLocalStorage();
- return reject();
- });
+ });
});
}
@@ -178,7 +187,7 @@ export class CorpusService {
return new Promise(((resolve, reject) => {
const url: string = configMC.backendBaseUrl + configMC.backendApiRawtextPath;
const params: HttpParams = new HttpParams().set('urn', urn);
- HelperService.makeGetRequest(this.http, this.toastCtrl, url, params).then((ar: AnnisResponse) => {
+ this.helperService.makeGetRequest(this.http, this.toastCtrl, url, params).then((ar: AnnisResponse) => {
return resolve(ar);
}, (error: HttpErrorResponse) => {
return reject(error);
@@ -190,7 +199,7 @@ export class CorpusService {
return new Promise((resolve, reject) => {
const fullUrl: string = configMC.backendBaseUrl + configMC.backendApiValidReffPath;
const params: HttpParams = new HttpParams().set('urn', urn);
- HelperService.makeGetRequest(this.http, this.toastCtrl, fullUrl, params).then((reff: string[]) => {
+ this.helperService.makeGetRequest(this.http, this.toastCtrl, fullUrl, params).then((reff: string[]) => {
resolve(reff);
}, (error: HttpErrorResponse) => {
reject(error);
@@ -198,17 +207,16 @@ export class CorpusService {
});
}
- getFrequencyAnalysis() {
+ getFrequencyAnalysis(): Promise {
return new Promise((resolve, reject) => {
if (this.annisResponse.frequency_analysis.length) {
return resolve();
} else {
const url: string = configMC.backendBaseUrl + configMC.backendApiFrequencyPath;
const params: HttpParams = new HttpParams().set('urn', this.currentUrn);
- HelperService.makeGetRequest(this.http, this.toastCtrl, url, params).then((fis: FrequencyItem[]) => {
- HelperService.isLoading = false;
+ this.helperService.makeGetRequest(this.http, this.toastCtrl, url, params).then((fis: FrequencyItem[]) => {
this.annisResponse.frequency_analysis = fis;
- HelperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
+ this.helperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
state.mostRecentSetup.annisResponse = this.annisResponse;
this.helperService.saveApplicationState(state).then();
});
@@ -228,13 +236,13 @@ export class CorpusService {
x => x.values[0] === this.exercise.queryItems[0].values[0] &&
x.phenomena[1] === query.phenomenon.toString());
return Array.from(new Set(relevantFIs.map(x => x.values[1]))).sort((a, b) => {
- return a === b ? 0 : (pmc.translationValues[a] < pmc.translationValues[b] ? -1 : 1);
+ return pmc.translationValues[a] < pmc.translationValues[b] ? -1 : 1;
});
} else {
const relevantFIs: FrequencyItem[] = this.annisResponse.frequency_analysis.filter(
x => x.phenomena[0] === query.phenomenon.toString());
return Array.from(new Set(relevantFIs.map(x => x.values[0]))).sort((a, b) => {
- return a === b ? 0 : (pmc.translationValues[a] < pmc.translationValues[b] ? -1 : 1);
+ return pmc.translationValues[a] < pmc.translationValues[b] ? -1 : 1;
});
}
}
@@ -242,14 +250,14 @@ export class CorpusService {
return [];
}
return Object.keys(pmc.specificValues).sort((a, b) => {
- return a === b ? 0 : (pmc.translationValues[a] < pmc.translationValues[b] ? -1 : 1);
+ return pmc.translationValues[a] < pmc.translationValues[b] ? -1 : 1;
});
}
getText(saveToCache: boolean = true): Promise {
return new Promise((resolve, reject) => {
this.currentText = '';
- if (HelperService.isVocabularyCheck) {
+ if (this.helperService.isVocabularyCheck) {
return resolve();
} else {
this.getCTStextPassage(this.currentUrn).then((ar: AnnisResponse) => {
@@ -262,7 +270,7 @@ export class CorpusService {
});
}
- getTranslations() {
+ getTranslations(): void {
this.translate.get('ERROR_CORPORA_UNAVAILABLE').subscribe(value => this.corporaUnavailableString = value);
this.translate.get('EXERCISE_FEEDBACK_CORRECT_DEFAULT').subscribe(value => this.exercise.feedback.correct = value);
this.translate.get('EXERCISE_FEEDBACK_INCORRECT_DEFAULT').subscribe(value => this.exercise.feedback.incorrect = value);
@@ -270,66 +278,71 @@ export class CorpusService {
this.exercise.feedback.partiallyCorrect = value;
});
this.translate.get('EXERCISE_FEEDBACK_GENERAL_DEFAULT').subscribe(value => this.exercise.feedback.general = value);
- this.translate.get(ExerciseTypeTranslation[ExerciseType.cloze]).subscribe(value => this.exercise.typeTranslation = value);
- this.translate.get(InstructionsTranslation[ExerciseType.cloze]).subscribe((value) => {
+ this.translate.get(ExerciseTypeTranslation[this.exercise.type]).subscribe(value => this.exercise.typeTranslation = value);
+ this.translate.get(InstructionsTranslation[this.exercise.type]).subscribe((value) => {
this.exercise.instructionsTranslation = value;
});
this.translate.get('INVALID_TEXT_RANGE').subscribe(value => this.invalidTextRangeString = value);
this.translate.get('ERROR_CITATIONS_UNAVAILABLE').subscribe(value => this.citationsUnavailableString = value);
this.translate.get('LINK_COPIED').subscribe(value => this.shareLinkCopiedString = value);
+ this.translate.get('TEXT_TOO_LONG').subscribe(value => this.textTooLongString = value + configMC.maxTextLength);
+ this.translate.get('QUERY_VALUE_EMPTY').subscribe(value => this.emptyQueryValueString = value);
+ this.translate.get('DATA_SENT').subscribe(value => this.dataSentSuccessMessage = value);
+ this.translate.get('DATA_ALREADY_SENT').subscribe(value => this.dataAlreadySentMessage = value);
+ this.translate.get('SEARCH_REGEX_MISSING').subscribe(value => this.searchRegexMissingString = value);
+ this.translate.get('TOO_MANY_SEARCH_RESULTS').subscribe(value => this.tooManyHitsString = value);
}
initCorpusService(): Promise {
- return new Promise((resolve) => {
- this.isMostRecentSetupLoaded = false;
- this.helperService.initApplicationState();
- this.initCurrentCorpus();
+ return new Promise((resolve, reject) => {
+ this.initCurrentCorpus().then();
this.initCurrentTextRange();
this.initUpdateInfo().then(() => {
this.checkForUpdates().finally(() => {
this.checkAnnisResponse().then(() => {
this.restoreLastCorpus().then(() => {
- this.isMostRecentSetupLoaded = true;
return resolve();
});
- }, () => {
- this.isMostRecentSetupLoaded = true;
- return resolve();
});
});
+ }, () => {
+ return reject();
});
this.initPhenomenonMap();
- this.getTranslations();
this.translate.onLangChange.subscribe(() => {
this.getTranslations();
});
});
}
- initCurrentCorpus(): void {
- this.currentCorpus = new ReplaySubject(1);
- if (!this.currentCorpusCache) {
- HelperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
- const textData: TextData = state.currentSetup ? state.currentSetup : state.mostRecentSetup;
- this.currentCorpusCache = textData ? textData.currentCorpus : null;
- if (this.currentCorpusCache) {
- this.currentCorpus.next(this.currentCorpusCache);
- }
- });
- } else {
- this.currentCorpus.next(this.currentCorpusCache);
- }
+ initCurrentCorpus(): Promise {
+ return new Promise((resolve) => {
+ this.currentCorpus = new ReplaySubject(1);
+ if (!this.currentCorpusCache) {
+ this.helperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
+ const textData: TextData = state.currentSetup ? state.currentSetup : state.mostRecentSetup;
+ this.currentCorpusCache = textData ? textData.currentCorpus : null;
+ if (this.currentCorpusCache) {
+ this.currentCorpus.next(this.currentCorpusCache);
+ }
+ return resolve();
+ });
+ } else {
+ this.currentCorpus.next(this.currentCorpusCache);
+ return resolve();
+ }
+ });
}
initCurrentTextRange(): void {
this.currentTextRange = new ReplaySubject(1);
- HelperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
+ this.helperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
this.currentTextRangeCache = state.currentSetup.currentTextRange;
this.currentTextRange.next(this.currentTextRangeCache);
});
}
- initPhenomenonMap() {
+ initPhenomenonMap(): void {
// map the different phenomena to their respective Enum for processing and display/translation
Object.keys(Phenomenon).forEach((key) => {
if (key !== Phenomenon[Phenomenon.lemma]) {
@@ -362,7 +375,7 @@ export class CorpusService {
});
}
- isTreebank(corpus: CorpusMC) {
+ isTreebank(corpus: CorpusMC): boolean {
return corpus.source_urn.includes('proiel');
}
@@ -378,7 +391,7 @@ export class CorpusService {
});
}
- processAnnisResponse(ar: AnnisResponse, saveToCache: boolean = true) {
+ processAnnisResponse(ar: AnnisResponse, saveToCache: boolean = true): void {
Object.keys(this.phenomenonMap).forEach((key: string) => {
const pmc: PhenomenonMapContent = this.phenomenonMap[key];
pmc.specificValues = {};
@@ -387,7 +400,7 @@ export class CorpusService {
this.processNodes(ar);
const pointingLinks: LinkMC[] = ar.links.filter(x => x.annis_component_type === 'Pointing');
pointingLinks.forEach((link: LinkMC) => {
- const dep: DependencyValue = HelperService.dependencyMap[link.udep_deprel];
+ const dep: DependencyValue = this.helperService.dependencyMap[link.udep_deprel];
if (dep) {
const existingValue = this.phenomenonMap.dependency.specificValues[dep];
this.phenomenonMap.dependency.specificValues[dep] = (existingValue ? existingValue : 0) + 1;
@@ -405,7 +418,7 @@ export class CorpusService {
});
this.annisResponse = ar;
if (saveToCache) {
- HelperService.applicationState.pipe(take(1)).subscribe((as: ApplicationState) => {
+ this.helperService.applicationState.pipe(take(1)).subscribe((as: ApplicationState) => {
as.currentSetup.annisResponse = null;
as.mostRecentSetup.currentUrn = this.currentUrn;
as.mostRecentSetup.annisResponse = ar;
@@ -417,7 +430,7 @@ export class CorpusService {
}
}
- processCorpora(corpusList: CorpusMC[]) {
+ processCorpora(corpusList: CorpusMC[]): void {
this.availableCorpora = [];
this.availableAuthors = [];
corpusList.forEach((corpus: CorpusMC) => {
@@ -442,7 +455,6 @@ export class CorpusService {
} else if (author1.name > author2.name) {
return 1;
}
- return 0;
});
this.availableAuthors.forEach((author) => {
author.corpora.sort((corpus1, corpus2) => {
@@ -456,7 +468,7 @@ export class CorpusService {
});
}
- processNodes(ar: AnnisResponse) {
+ processNodes(ar: AnnisResponse): void {
ar.nodes.forEach((node: NodeMC) => {
let existingValue = this.phenomenonMap.lemma.specificValues[node.udep_lemma];
this.phenomenonMap.lemma.specificValues[node.udep_lemma] = (existingValue ? existingValue : 0) + 1;
@@ -466,20 +478,20 @@ export class CorpusService {
const casePart: string = featsParts.find(x => x.toLowerCase().includes(Phenomenon[Phenomenon.case]));
if (casePart) {
const caseAbbreviation: string = casePart.split('=')[1];
- const caseValue: CaseValue = HelperService.caseMap[caseAbbreviation];
+ const caseValue: CaseValue = this.helperService.caseMap[caseAbbreviation];
existingValue = this.phenomenonMap.case.specificValues[caseValue];
this.phenomenonMap.case.specificValues[caseValue] = (existingValue ? existingValue : 0) + 1;
}
}
- const pos: PartOfSpeechValue = HelperService.partOfSpeechMap[node.udep_upostag];
+ const pos: PartOfSpeechValue = this.helperService.partOfSpeechMap[node.udep_upostag];
existingValue = this.phenomenonMap.partOfSpeech.specificValues[pos];
this.phenomenonMap.partOfSpeech.specificValues[pos] = (existingValue ? existingValue : 0) + 1;
});
}
- restoreLastCorpus() {
+ restoreLastCorpus(): Promise {
return new Promise((resolve, reject) => {
- HelperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
+ this.helperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
this.annisResponse = state.mostRecentSetup.annisResponse;
this.currentUrn = state.mostRecentSetup.currentUrn;
this.currentCorpusCache = state.mostRecentSetup.currentCorpus;
@@ -503,13 +515,13 @@ export class CorpusService {
});
}
- saveNewCorpus(corpus: CorpusMC): void {
+ setCurrentCorpus(corpus: CorpusMC): void {
this.currentCorpusCache = corpus;
this.currentCorpus.next(this.currentCorpusCache);
this.setCurrentTextRange(-1, null, new TextRange({start: ['', '', ''], end: ['', '', '']}));
- HelperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
+ this.helperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
state.currentSetup.currentCorpus = corpus;
- HelperService.applicationState.next(state);
+ this.helperService.applicationState.next(state);
});
}
@@ -529,7 +541,7 @@ export class CorpusService {
this.currentTextRange.next(this.currentTextRangeCache);
}
- updateBaseWord(query: QueryMC, queryIndex: number) {
+ updateBaseWord(query: QueryMC, queryIndex: number): void {
if (!this.annisResponse || !this.annisResponse.frequency_analysis || !this.annisResponse.frequency_analysis.length) {
return;
}
diff --git a/src/app/doc-exercises/doc-exercises.page.html b/src/app/doc-exercises/doc-exercises.page.html
index 307fbe3..553dad3 100644
--- a/src/app/doc-exercises/doc-exercises.page.html
+++ b/src/app/doc-exercises/doc-exercises.page.html
@@ -2,12 +2,12 @@
-
+
{{ 'DOC_EXERCISES' | translate }}
@@ -118,19 +118,19 @@
-
+
{{ 'ABOUT' | translate }}
-
+
{{ 'DOC_SOFTWARE' | translate}}
-
+
{{'DOC_VOC_UNIT' | translate}}
-
+
{{ 'IMPRINT' | translate }}
diff --git a/src/app/doc-exercises/doc-exercises.page.ts b/src/app/doc-exercises/doc-exercises.page.ts
index 114b26b..466d85c 100644
--- a/src/app/doc-exercises/doc-exercises.page.ts
+++ b/src/app/doc-exercises/doc-exercises.page.ts
@@ -13,12 +13,12 @@ import {ExerciseDocumentation} from 'src/app/models/exerciseDocumentation';
export class DocExercisesPage implements OnInit {
exerciseGenDocs: ExerciseDocumentation[] = [];
- HelperService = HelperService;
vocUnitDocs: ExerciseDocumentation[] = [];
constructor(public navCtrl: NavController,
public http: HttpClient,
- public translate: TranslateService) {
+ public translate: TranslateService,
+ public helperService: HelperService) {
}
ngOnInit(): void {
diff --git a/src/app/doc-software/doc-software.page.html b/src/app/doc-software/doc-software.page.html
index 328fc14..6db0913 100644
--- a/src/app/doc-software/doc-software.page.html
+++ b/src/app/doc-software/doc-software.page.html
@@ -2,12 +2,12 @@
-
+
{{ 'DOC_SOFTWARE' | translate }}
@@ -101,19 +101,19 @@
-
+
{{ 'ABOUT' | translate }}
-
+
{{'DOC_EXERCISES' | translate}}
-
+
{{'DOC_VOC_UNIT' | translate}}
-
+
{{ 'IMPRINT' | translate }}
diff --git a/src/app/doc-software/doc-software.page.ts b/src/app/doc-software/doc-software.page.ts
index 46b80b4..095fb95 100644
--- a/src/app/doc-software/doc-software.page.ts
+++ b/src/app/doc-software/doc-software.page.ts
@@ -12,10 +12,10 @@ import {TranslateService} from '@ngx-translate/core';
export class DocSoftwarePage {
devIndices: number[] = [...Array(13).keys()];
- HelperService = HelperService;
constructor(public navCtrl: NavController,
public http: HttpClient,
- public translate: TranslateService) {
+ public translate: TranslateService,
+ public helperService: HelperService) {
}
}
diff --git a/src/app/doc-voc-unit/doc-voc-unit.page.html b/src/app/doc-voc-unit/doc-voc-unit.page.html
index 52f1d40..ee70a65 100644
--- a/src/app/doc-voc-unit/doc-voc-unit.page.html
+++ b/src/app/doc-voc-unit/doc-voc-unit.page.html
@@ -2,12 +2,12 @@
-
+
{{ 'DOC_VOC_UNIT' | translate }}
@@ -95,19 +95,19 @@
-
+
{{ 'ABOUT' | translate }}
-
+
{{ 'DOC_SOFTWARE' | translate}}
-
+
{{'DOC_EXERCISES' | translate}}
-
+
{{ 'IMPRINT' | translate }}
diff --git a/src/app/doc-voc-unit/doc-voc-unit.page.ts b/src/app/doc-voc-unit/doc-voc-unit.page.ts
index c106c30..2c3678e 100644
--- a/src/app/doc-voc-unit/doc-voc-unit.page.ts
+++ b/src/app/doc-voc-unit/doc-voc-unit.page.ts
@@ -11,13 +11,13 @@ import {TranslateService} from '@ngx-translate/core';
})
export class DocVocUnitPage {
- HelperService = HelperService;
hypothesisIndices: number[] = [...Array(5).keys()];
questionsIndices: number[] = [...Array(10).keys()];
sequenceIndices: number[] = [...Array(6).keys()];
constructor(public navCtrl: NavController,
public http: HttpClient,
- public translate: TranslateService) {
+ public translate: TranslateService,
+ public helperService: HelperService) {
}
}
diff --git a/src/app/exercise-list/exercise-list.page.html b/src/app/exercise-list/exercise-list.page.html
index e4edf80..56ce5ca 100644
--- a/src/app/exercise-list/exercise-list.page.html
+++ b/src/app/exercise-list/exercise-list.page.html
@@ -2,12 +2,12 @@
-
+
{{ 'EXERCISE_LIST' | translate }}
@@ -26,7 +26,7 @@
+ class="searchbar" placeholder="{{ 'BROWSE' | translate }}" [(ngModel)]="currentSearchValue"/>
@@ -35,7 +35,7 @@
{{ 'SORT_BY' | translate }}
-
+
{{ key | translate }}
@@ -139,7 +139,7 @@
- {{ 'NO_EXERCISES_FOUND' | translate }}
+ {{ 'NO_EXERCISES_FOUND' | translate }}
diff --git a/src/app/exercise-list/exercise-list.page.spec.ts b/src/app/exercise-list/exercise-list.page.spec.ts
index ee4280d..4d4c444 100644
--- a/src/app/exercise-list/exercise-list.page.spec.ts
+++ b/src/app/exercise-list/exercise-list.page.spec.ts
@@ -9,10 +9,19 @@ import {TranslateTestingModule} from '../translate-testing/translate-testing.mod
import {FormsModule} from '@angular/forms';
import {IonicModule} from '@ionic/angular';
import {APP_BASE_HREF} from '@angular/common';
+import {ExerciseType, MoodleExerciseType, SortingCategory, VocabularyCorpus} from '../models/enum';
+import MockMC from '../models/mockMC';
+import {ApplicationState} from '../models/applicationState';
+import {ExerciseMC} from '../models/exerciseMC';
+import {AnnisResponse} from '../models/annisResponse';
+import Spy = jasmine.Spy;
+import configMC from '../../configMC';
+import {UpdateInfo} from '../models/updateInfo';
describe('ExerciseListPage', () => {
- let component: ExerciseListPage;
+ let exerciseListPage: ExerciseListPage;
let fixture: ComponentFixture
;
+ let getExerciseListSpy: Spy;
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -30,18 +39,99 @@ describe('ExerciseListPage', () => {
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
- .compileComponents();
+ .compileComponents().then();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ExerciseListPage);
- component = fixture.componentInstance;
- spyOn(component, 'getExerciseList');
+ exerciseListPage = fixture.componentInstance;
+ getExerciseListSpy = spyOn(exerciseListPage, 'getExerciseList').and.returnValue(Promise.resolve());
fixture.detectChanges();
});
it('should create', () => {
- expect(component).toBeTruthy();
- expect(component.getExerciseList).toHaveBeenCalled();
+ expect(exerciseListPage).toBeTruthy();
+ expect(exerciseListPage.getExerciseList).toHaveBeenCalled();
+ });
+
+ it('should filter exercises', () => {
+ exerciseListPage.metadata.eid = 'test';
+ exerciseListPage.availableExercises = [new ExerciseMC({eid: 'eid'})];
+ exerciseListPage.filterExercises('test');
+ expect(exerciseListPage.exercises.length).toBe(1);
+ });
+
+ it('should get a date string', () => {
+ const dateString: string = exerciseListPage.getDateString(0);
+ expect(dateString).toBe('1/1/1970');
+ });
+
+ it('should get the exercise list', (done) => {
+ getExerciseListSpy.and.callThrough();
+ const requestSpy: Spy = spyOn(exerciseListPage.helperService, 'makeGetRequest').and.callFake(() => Promise.reject());
+ spyOn(exerciseListPage.storage, 'get').withArgs(configMC.localStorageKeyUpdateInfo).and.returnValue(
+ Promise.resolve(JSON.stringify(new UpdateInfo({exerciseList: 0}))));
+ exerciseListPage.getExerciseList().then(() => {
+ }, () => {
+ expect(requestSpy).toHaveBeenCalledTimes(1);
+ exerciseListPage.vocService.currentReferenceVocabulary = VocabularyCorpus.agldt;
+ exerciseListPage.helperService.applicationState.next(new ApplicationState({exerciseList: []}));
+ requestSpy.and.returnValue(Promise.resolve([]));
+ exerciseListPage.getExerciseList().then(() => {
+ expect(exerciseListPage.availableExercises.length).toBe(0);
+ exerciseListPage.helperService.applicationState.next(exerciseListPage.helperService.deepCopy(MockMC.applicationState));
+ requestSpy.and.returnValue(Promise.resolve([new ExerciseMC()]));
+ exerciseListPage.getExerciseList().then(() => {
+ expect(exerciseListPage.availableExercises.length).toBe(1);
+ done();
+ });
+ });
+ });
+ });
+
+ it('should get the matching degree', () => {
+ let degree: string = exerciseListPage.getMatchingDegree(new ExerciseMC({matching_degree: 20}));
+ expect(degree).toBe('20');
+ degree = exerciseListPage.getMatchingDegree(new ExerciseMC());
+ expect(degree).toBeFalsy();
+ });
+
+ it('should show an exercise', (done) => {
+ const requestSpy: Spy = spyOn(exerciseListPage.helperService, 'makeGetRequest').and.callFake(() => Promise.reject());
+ spyOn(exerciseListPage.helperService, 'goToPreviewPage').and.returnValue(Promise.resolve(true));
+ exerciseListPage.showExercise(new ExerciseMC()).then(() => {
+ }, () => {
+ requestSpy.and.returnValue(Promise.resolve(new AnnisResponse()));
+ exerciseListPage.showExercise(new ExerciseMC({exercise_type: MoodleExerciseType.markWords.toString()})).then(() => {
+ expect(exerciseListPage.corpusService.exercise.type).toBe(ExerciseType.markWords);
+ done();
+ });
+ });
+ });
+
+ it('should sort exercises', () => {
+ let oldList: ExerciseMC[] = [new ExerciseMC({eid: 'a'}), new ExerciseMC({eid: 'b'})];
+ exerciseListPage.exercises = [new ExerciseMC({eid: 'a'}), new ExerciseMC({eid: 'b'})];
+ exerciseListPage.currentSortingCategory = SortingCategory.vocAsc;
+ exerciseListPage.sortExercises();
+ expect(exerciseListPage.exercises).toEqual(oldList);
+ oldList = [new ExerciseMC({matching_degree: 1}), new ExerciseMC({matching_degree: 0}),
+ new ExerciseMC({matching_degree: 0}), new ExerciseMC({matching_degree: 2})];
+ exerciseListPage.exercises = [new ExerciseMC({matching_degree: 1}), new ExerciseMC({matching_degree: 0}),
+ new ExerciseMC({matching_degree: 0}), new ExerciseMC({matching_degree: 2})];
+ exerciseListPage.sortExercises();
+ expect(exerciseListPage.exercises).not.toEqual(oldList);
+ exerciseListPage.currentSortingCategory = SortingCategory.vocDesc;
+ exerciseListPage.exercises.splice(0, 0, new ExerciseMC({matching_degree: 1}));
+ exerciseListPage.sortExercises();
+ expect(exerciseListPage.exercises).not.toEqual(oldList);
+ });
+
+ it('should toggle the vocabulary corpus display', () => {
+ exerciseListPage.showVocabularyCorpus = false;
+ exerciseListPage.toggleVocCorpus();
+ expect(exerciseListPage.showVocabularyCorpus).toBe(true);
+ exerciseListPage.toggleVocCorpus();
+ expect(exerciseListPage.showVocabularyCorpus).toBe(false);
});
});
diff --git a/src/app/exercise-list/exercise-list.page.ts b/src/app/exercise-list/exercise-list.page.ts
index 7e09dc5..294b842 100644
--- a/src/app/exercise-list/exercise-list.page.ts
+++ b/src/app/exercise-list/exercise-list.page.ts
@@ -35,7 +35,6 @@ export class ExerciseListPage implements OnInit {
public exercises: ExerciseMC[] = [];
public ExerciseTypeTranslation = ExerciseTypeTranslation;
public hasVocChanged = false;
- public HelperService = HelperService;
public Math = Math;
public metadata: { [eid: string]: string } = {};
public ObjectKeys = Object.keys;
@@ -70,7 +69,7 @@ export class ExerciseListPage implements OnInit {
public vocService: VocabularyService) {
}
- filterExercises(searchValue: string) {
+ filterExercises(searchValue: string): void {
if (!searchValue) {
this.exercises = this.availableExercises;
} else {
@@ -83,35 +82,39 @@ export class ExerciseListPage implements OnInit {
this.sortExercises();
}
- getDateString(dateMilliseconds: number) {
+ getDateString(dateMilliseconds: number): string {
return new Date(dateMilliseconds).toLocaleDateString();
}
- getExerciseList(): void {
- const url: string = configMC.backendBaseUrl + configMC.backendApiExerciseListPath;
- this.hasVocChanged = false;
- let params: HttpParams = new HttpParams().set('lang', this.translateService.currentLang);
- HelperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
- this.storage.get(configMC.localStorageKeyUpdateInfo).then((jsonString: string) => {
- const updateInfo: UpdateInfo = JSON.parse(jsonString) as UpdateInfo;
- // if there are no exercises in the cache, force refresh
- const lut: number = state.exerciseList.length ? updateInfo.exerciseList : 0;
- params = params.set('last_update_time', lut.toString());
- if (this.vocService.currentReferenceVocabulary) {
- params = params.set('vocabulary', VocabularyCorpus[this.vocService.currentReferenceVocabulary]);
- params = params.set('frequency_upper_bound', this.vocService.frequencyUpperBound.toString());
- }
- HelperService.makeGetRequest(this.http, this.toastCtrl, url, params).then((exercises: ExerciseMC[]) => {
- updateInfo.exerciseList = new Date().getTime();
- this.storage.set(configMC.localStorageKeyUpdateInfo, JSON.stringify(updateInfo)).then();
- if (exercises.length) {
- state.exerciseList = this.availableExercises = this.exercises = exercises;
- this.helperService.saveApplicationState(state).then();
- } else {
- this.availableExercises = this.exercises = state.exerciseList;
+ getExerciseList(): Promise {
+ return new Promise((resolve, reject) => {
+ const url: string = configMC.backendBaseUrl + configMC.backendApiExerciseListPath;
+ this.hasVocChanged = false;
+ let params: HttpParams = new HttpParams().set('lang', this.translateService.currentLang);
+ this.helperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
+ this.storage.get(configMC.localStorageKeyUpdateInfo).then((jsonString: string) => {
+ const updateInfo: UpdateInfo = JSON.parse(jsonString) as UpdateInfo;
+ // if there are no exercises in the cache, force refresh
+ const lastUpdateTime: number = state.exerciseList.length ? updateInfo.exerciseList : 0;
+ params = params.set('last_update_time', lastUpdateTime.toString());
+ if (this.vocService.currentReferenceVocabulary) {
+ params = params.set('vocabulary', VocabularyCorpus[this.vocService.currentReferenceVocabulary]);
+ params = params.set('frequency_upper_bound', this.vocService.frequencyUpperBound.toString());
}
- this.processExercises();
- }, () => {
+ this.helperService.makeGetRequest(this.http, this.toastCtrl, url, params).then((exercises: ExerciseMC[]) => {
+ updateInfo.exerciseList = new Date().getTime();
+ this.storage.set(configMC.localStorageKeyUpdateInfo, JSON.stringify(updateInfo)).then();
+ if (exercises.length) {
+ state.exerciseList = this.availableExercises = this.exercises = exercises;
+ this.helperService.saveApplicationState(state).then();
+ } else {
+ this.availableExercises = this.exercises = state.exerciseList;
+ }
+ this.processExercises();
+ return resolve();
+ }, () => {
+ return reject();
+ });
});
});
});
@@ -123,10 +126,10 @@ export class ExerciseListPage implements OnInit {
ngOnInit(): void {
this.vocService.currentReferenceVocabulary = null;
- this.getExerciseList();
+ this.getExerciseList().then();
}
- public processExercises() {
+ public processExercises(): void {
this.exercises.forEach((exercise: ExerciseMC) => {
this.translateService.get(ExerciseTypeTranslation[MoodleExerciseType[exercise.exercise_type]]).subscribe(
value => exercise.exercise_type_translation = value);
@@ -136,17 +139,21 @@ export class ExerciseListPage implements OnInit {
this.filterExercises(this.currentSearchValue);
}
- showExercise(exercise: ExerciseMC) {
- const url: string = configMC.backendBaseUrl + configMC.backendApiExercisePath;
- const params: HttpParams = new HttpParams().set('eid', exercise.eid);
- HelperService.makeGetRequest(this.http, this.toastCtrl, url, params).then((ar: AnnisResponse) => {
- // save this exercise only locally in the CorpusService (not as MostRecentSetup in the HelperService) because
- // users just want to have a quick look at it
- this.corpusService.annisResponse = ar;
- const met: MoodleExerciseType = MoodleExerciseType[exercise.exercise_type];
- this.corpusService.exercise.type = ExerciseType[met.toString()];
- HelperService.goToPreviewPage(this.navCtrl).then();
- }, () => {
+ showExercise(exercise: ExerciseMC): Promise {
+ return new Promise((resolve, reject) => {
+ const url: string = configMC.backendBaseUrl + configMC.backendApiExercisePath;
+ const params: HttpParams = new HttpParams().set('eid', exercise.eid);
+ this.helperService.makeGetRequest(this.http, this.toastCtrl, url, params).then((ar: AnnisResponse) => {
+ // save this exercise only locally in the CorpusService (not as MostRecentSetup in the HelperService) because
+ // users just want to have a quick look at it
+ this.corpusService.annisResponse = ar;
+ const met: MoodleExerciseType = MoodleExerciseType[exercise.exercise_type];
+ this.corpusService.exercise.type = ExerciseType[met.toString()];
+ this.helperService.goToPreviewPage(this.navCtrl).then();
+ return resolve();
+ }, () => {
+ return reject();
+ });
});
}
@@ -167,13 +174,13 @@ export class ExerciseListPage implements OnInit {
}
}
- toggleVocCorpus() {
+ toggleVocCorpus(): void {
this.showVocabularyCorpus = !this.showVocabularyCorpus;
if (this.showVocabularyCorpus && !this.vocService.currentReferenceVocabulary) {
this.vocService.currentReferenceVocabulary = VocabularyCorpus.bws;
this.hasVocChanged = true;
}
- const iRowElement: HTMLElement = document.querySelector('#vocCorpus');
+ const iRowElement: HTMLIonRowElement = document.querySelector('#vocCorpus');
iRowElement.style.display = this.showVocabularyCorpus ? 'block' : 'none';
}
}
diff --git a/src/app/exercise-parameters/exercise-parameters.page.html b/src/app/exercise-parameters/exercise-parameters.page.html
index bfba088..b91977d 100644
--- a/src/app/exercise-parameters/exercise-parameters.page.html
+++ b/src/app/exercise-parameters/exercise-parameters.page.html
@@ -8,12 +8,12 @@
-
+
{{ 'EXERCISE_PARAMETERS' | translate }}
diff --git a/src/app/exercise-parameters/exercise-parameters.page.spec.ts b/src/app/exercise-parameters/exercise-parameters.page.spec.ts
index fec9736..3d1fdf9 100644
--- a/src/app/exercise-parameters/exercise-parameters.page.spec.ts
+++ b/src/app/exercise-parameters/exercise-parameters.page.spec.ts
@@ -1,5 +1,5 @@
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
-import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {async, ComponentFixture, fakeAsync, flushMicrotasks, TestBed} from '@angular/core/testing';
import {ExerciseParametersPage} from './exercise-parameters.page';
import {HttpClientModule} from '@angular/common/http';
@@ -8,9 +8,20 @@ import {RouterModule} from '@angular/router';
import {TranslateTestingModule} from '../translate-testing/translate-testing.module';
import {FormsModule} from '@angular/forms';
import {APP_BASE_HREF} from '@angular/common';
+import {AnnisResponse} from '../models/annisResponse';
+import {TextRange} from '../models/textRange';
+import {ReplaySubject} from 'rxjs';
+import configMC from '../../configMC';
+import {ExerciseType, PartOfSpeechValue, Phenomenon} from '../models/enum';
+import {ToastController} from '@ionic/angular';
+import {QueryMC} from '../models/queryMC';
+import {PhenomenonMapContent} from '../models/phenomenonMap';
+import {FrequencyItem} from '../models/frequencyItem';
+import Spy = jasmine.Spy;
+import MockMC from '../models/mockMC';
describe('ExerciseParametersPage', () => {
- let component: ExerciseParametersPage;
+ let exerciseParametersPage: ExerciseParametersPage;
let fixture: ComponentFixture;
beforeEach(async(() => {
@@ -25,19 +36,122 @@ describe('ExerciseParametersPage', () => {
],
providers: [
{provide: APP_BASE_HREF, useValue: '/'},
+ {provide: ToastController, useValue: MockMC.toastController},
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
- .compileComponents();
+ .compileComponents().then();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ExerciseParametersPage);
- component = fixture.componentInstance;
+ exerciseParametersPage = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
- expect(component).toBeTruthy();
+ expect(exerciseParametersPage).toBeTruthy();
+ });
+
+ it('should generate an exercise', (done) => {
+ exerciseParametersPage.corpusService.annisResponse = new AnnisResponse({solutions: []});
+ exerciseParametersPage.corpusService.initCurrentCorpus().then(() => {
+ exerciseParametersPage.corpusService.currentTextRange = new ReplaySubject(1);
+ exerciseParametersPage.corpusService.currentTextRange.next(new TextRange({start: [], end: []}));
+ const h5pSpy: Spy = spyOn(exerciseParametersPage, 'getH5Pexercise').and.returnValue(Promise.resolve());
+ exerciseParametersPage.generateExercise().then(() => {
+ expect(exerciseParametersPage.corpusService.annisResponse.solutions).toBeFalsy();
+ expect(h5pSpy).toHaveBeenCalledTimes(1);
+ configMC.maxTextLength = 1;
+ exerciseParametersPage.corpusService.currentText = 'text';
+ exerciseParametersPage.generateExercise().then(() => {
+ }, () => {
+ expect(h5pSpy).toHaveBeenCalledTimes(1);
+ configMC.maxTextLength = 0;
+ exerciseParametersPage.corpusService.exercise.queryItems[0].phenomenon = Phenomenon.lemma;
+ exerciseParametersPage.corpusService.exercise.type = ExerciseType.matching;
+ exerciseParametersPage.corpusService.exercise.queryItems.push(new QueryMC({values: []}));
+ exerciseParametersPage.generateExercise().then(() => {
+ }, () => {
+ expect(h5pSpy).toHaveBeenCalledTimes(1);
+ done();
+ });
+ });
+ });
+ });
+ });
+
+ it('should get a display value', () => {
+ const pmc: PhenomenonMapContent = exerciseParametersPage.corpusService.phenomenonMap[Phenomenon.lemma];
+ pmc.translationValues = {key: 'value'};
+ pmc.specificValues = {key: 1};
+ const key = 'key';
+ let displayValue: string = exerciseParametersPage.getDisplayValue(new QueryMC({phenomenon: Phenomenon.lemma}), key);
+ expect(displayValue.length).toBe(9);
+ exerciseParametersPage.corpusService.exercise.type = ExerciseType.matching;
+ exerciseParametersPage.corpusService.annisResponse = new AnnisResponse({
+ frequency_analysis: [new FrequencyItem({values: [PartOfSpeechValue.adjective.toString(), key], count: 10})]
+ });
+ displayValue = exerciseParametersPage.getDisplayValue(new QueryMC({phenomenon: Phenomenon.lemma}), key, 1);
+ expect(displayValue.length).toBe(10);
+ exerciseParametersPage.corpusService.annisResponse.frequency_analysis[0] = new FrequencyItem({
+ phenomena: [Phenomenon.lemma.toString()],
+ values: [key],
+ count: 100
+ });
+ exerciseParametersPage.corpusService.annisResponse.frequency_analysis.push(
+ exerciseParametersPage.corpusService.annisResponse.frequency_analysis[0]);
+ displayValue = exerciseParametersPage.getDisplayValue(new QueryMC({phenomenon: Phenomenon.lemma}), key, 0);
+ expect(displayValue.length).toBe(11);
+ });
+
+ it('should get exercise data', (done) => {
+ exerciseParametersPage.corpusService.initCurrentCorpus().then(() => {
+ exerciseParametersPage.corpusService.currentTextRange = new ReplaySubject(1);
+ exerciseParametersPage.corpusService.currentTextRange.next(new TextRange({start: ['', ''], end: ['', '']}));
+ const h5pSpy: Spy = spyOn(exerciseParametersPage, 'getH5Pexercise').and.returnValue(Promise.resolve());
+ const kwicSpy: Spy = spyOn(exerciseParametersPage, 'getKwicExercise').and.returnValue(Promise.resolve());
+ exerciseParametersPage.corpusService.exercise.type = ExerciseType.kwic;
+ exerciseParametersPage.getExerciseData();
+ expect(kwicSpy).toHaveBeenCalledTimes(1);
+ exerciseParametersPage.corpusService.exercise.type = ExerciseType.markWords;
+ const pmc: PhenomenonMapContent = exerciseParametersPage.corpusService.phenomenonMap[Phenomenon.partOfSpeech];
+ pmc.translationValues = {};
+ pmc.translationValues[PartOfSpeechValue.adjective.toString()] = '';
+ exerciseParametersPage.getExerciseData();
+ expect(h5pSpy).toHaveBeenCalledTimes(1);
+ done();
+ });
+ });
+
+ it('should get a H5P exercise', (done) => {
+ const requestSpy: Spy = spyOn(exerciseParametersPage.helperService, 'makePostRequest').and.returnValue(
+ Promise.resolve(new AnnisResponse()));
+ const navSpy: Spy = spyOn(exerciseParametersPage.helperService, 'goToPreviewPage').and.returnValue(Promise.resolve(true));
+ exerciseParametersPage.corpusService.annisResponse = new AnnisResponse();
+ exerciseParametersPage.getH5Pexercise(new FormData()).then(() => {
+ expect(navSpy).toHaveBeenCalledTimes(1);
+ requestSpy.and.callFake(() => Promise.reject());
+ exerciseParametersPage.getH5Pexercise(new FormData()).then(() => {
+ }, () => {
+ expect(navSpy).toHaveBeenCalledTimes(1);
+ done();
+ });
+ });
+ });
+
+ it('should get a KWIC exercise', (done) => {
+ const navSpy: Spy = spyOn(exerciseParametersPage.helperService, 'goToKwicPage').and.returnValue(Promise.resolve(true));
+ const requestSpy: Spy = spyOn(exerciseParametersPage.helperService, 'makePostRequest').and.returnValue(Promise.resolve('svg'));
+ exerciseParametersPage.getKwicExercise(new FormData()).then(() => {
+ expect(exerciseParametersPage.exerciseService.kwicGraphs.length).toBe(3);
+ expect(navSpy).toHaveBeenCalledTimes(1);
+ requestSpy.and.callFake(() => Promise.reject());
+ exerciseParametersPage.getKwicExercise(new FormData()).then(() => {
+ }, () => {
+ expect(navSpy).toHaveBeenCalledTimes(1);
+ done();
+ });
+ });
});
});
diff --git a/src/app/exercise-parameters/exercise-parameters.page.ts b/src/app/exercise-parameters/exercise-parameters.page.ts
index 7868ef0..8bf323b 100644
--- a/src/app/exercise-parameters/exercise-parameters.page.ts
+++ b/src/app/exercise-parameters/exercise-parameters.page.ts
@@ -29,10 +29,8 @@ import configMC from '../../configMC';
styleUrls: ['./exercise-parameters.page.scss'],
})
export class ExerciseParametersPage implements OnInit {
- emptyQueryValueString: string;
public ExerciseType = ExerciseType;
ExerciseTypeTranslation = ExerciseTypeTranslation;
- HelperService = HelperService;
public Math = Math;
ObjectKeys = Object.keys;
Phenomenon = Phenomenon;
@@ -47,33 +45,24 @@ export class ExerciseParametersPage implements OnInit {
public exerciseService: ExerciseService,
public http: HttpClient,
public helperService: HelperService) {
- this.translateService.onLangChange.subscribe(() => {
- this.initTranslation();
- });
- this.initTranslation();
}
- async generateExercise() {
- const phenomenon: Phenomenon = this.corpusService.exercise.queryItems[0].phenomenon;
- if (0 < configMC.maxTextLength && configMC.maxTextLength < this.corpusService.currentText.length) {
- const toast = await this.toastCtrl.create({
- message: this.textTooLongString,
- duration: 3000,
- position: 'top'
- });
- toast.present().then();
- } else if ((phenomenon === Phenomenon.lemma && !this.corpusService.exercise.queryItems[0].values) ||
- this.corpusService.exercise.type === ExerciseType.matching && !this.corpusService.exercise.queryItems[1].values[0]) {
- const toast = await this.toastCtrl.create({
- message: this.emptyQueryValueString,
- duration: 3000,
- position: 'top'
- });
- toast.present().then();
- } else {
- this.corpusService.annisResponse.solutions = null;
- this.getExerciseData();
- }
+ generateExercise(): Promise {
+ return new Promise((resolve, reject) => {
+ const phenomenon: Phenomenon = this.corpusService.exercise.queryItems[0].phenomenon;
+ if (0 < configMC.maxTextLength && configMC.maxTextLength < this.corpusService.currentText.length) {
+ this.helperService.showToast(this.toastCtrl, this.corpusService.textTooLongString).then();
+ return reject();
+ } else if ((phenomenon === Phenomenon.lemma && !this.corpusService.exercise.queryItems[0].values) ||
+ this.corpusService.exercise.type === ExerciseType.matching && !this.corpusService.exercise.queryItems[1].values[0]) {
+ this.helperService.showToast(this.toastCtrl, this.corpusService.emptyQueryValueString).then();
+ return reject();
+ } else {
+ this.corpusService.annisResponse.solutions = null;
+ this.getExerciseData();
+ return resolve();
+ }
+ });
}
public getDisplayValue(query: QueryMC, key: string, queryIndex: number = 0): string {
@@ -96,7 +85,7 @@ export class ExerciseParametersPage implements OnInit {
return translatedKey + ' (' + count + ')';
}
- getExerciseData() {
+ getExerciseData(): void {
const searchValues: string[] = this.corpusService.exercise.queryItems.map(
query => query.phenomenon + '=' + query.values.join('|'));
const formData = new FormData();
@@ -104,7 +93,7 @@ export class ExerciseParametersPage implements OnInit {
formData.append('search_values', JSON.stringify(searchValues));
let instructions: string = this.corpusService.exercise.instructionsTranslation;
if (this.corpusService.exercise.type === ExerciseType.kwic) {
- this.getKwicExercise(formData);
+ this.getKwicExercise(formData).then();
return;
} else if (this.corpusService.exercise.type === ExerciseType.markWords) {
const phenomenon: Phenomenon = this.corpusService.exercise.queryItems[0].phenomenon;
@@ -125,67 +114,47 @@ export class ExerciseParametersPage implements OnInit {
formData.append('incorrect_feedback', this.corpusService.exercise.feedback.incorrect);
formData.append('general_feedback', this.corpusService.exercise.feedback.general);
formData.append('work_author', cc.author);
- this.getH5Pexercise(formData);
+ this.getH5Pexercise(formData).then();
});
});
}
- getH5Pexercise(formData: FormData) {
- const url: string = configMC.backendBaseUrl + configMC.backendApiExercisePath;
- HelperService.currentError = null;
- HelperService.isLoading = true;
- this.http.post(url, formData).subscribe((ar: AnnisResponse) => {
- HelperService.isLoading = false;
- // save the old frequency analysis in case we want to change the exercise parameters at a later time
- ar.frequency_analysis = this.corpusService.annisResponse.frequency_analysis;
- HelperService.applicationState.pipe(take(1)).subscribe((as: ApplicationState) => {
- as.mostRecentSetup.annisResponse = ar;
- this.helperService.saveApplicationState(as).then();
- this.corpusService.annisResponse.exercise_id = ar.exercise_id;
- this.corpusService.annisResponse.uri = ar.uri;
- this.corpusService.annisResponse.solutions = ar.solutions;
- HelperService.goToPreviewPage(this.navCtrl).then();
+ getH5Pexercise(formData: FormData): Promise {
+ return new Promise((resolve, reject) => {
+ const url: string = configMC.backendBaseUrl + configMC.backendApiExercisePath;
+ this.helperService.makePostRequest(this.http, this.toastCtrl, url, formData).then((ar: AnnisResponse) => {
+ // save the old frequency analysis in case we want to change the exercise parameters at a later time
+ ar.frequency_analysis = this.corpusService.annisResponse.frequency_analysis;
+ this.helperService.applicationState.pipe(take(1)).subscribe((as: ApplicationState) => {
+ as.mostRecentSetup.annisResponse = ar;
+ this.helperService.saveApplicationState(as).then();
+ this.corpusService.annisResponse.exercise_id = ar.exercise_id;
+ this.corpusService.annisResponse.uri = ar.uri;
+ this.corpusService.annisResponse.solutions = ar.solutions;
+ this.helperService.goToPreviewPage(this.navCtrl).then();
+ return resolve();
+ });
+ }, () => {
+ return reject();
});
- }, async (error: HttpErrorResponse) => {
- HelperService.isLoading = false;
- HelperService.currentError = error;
- const toast = await this.toastCtrl.create({
- message: HelperService.generalErrorAlertMessage,
- duration: 3000,
- position: 'top'
- });
- toast.present().then();
});
}
- getKwicExercise(formData: FormData) {
- const kwicUrl: string = configMC.backendBaseUrl + configMC.backendApiKwicPath;
- HelperService.currentError = null;
- HelperService.isLoading = true;
- this.http.post(kwicUrl, formData).subscribe((svgString: string) => {
- HelperService.isLoading = false;
- this.exerciseService.kwicGraphs = svgString;
- this.navCtrl.navigateForward('kwic').then();
- }, async (error: HttpErrorResponse) => {
- HelperService.isLoading = false;
- HelperService.currentError = error;
- const toast = await this.toastCtrl.create({
- message: HelperService.generalErrorAlertMessage,
- duration: 3000,
- position: 'top'
+ getKwicExercise(formData: FormData): Promise {
+ return new Promise((resolve, reject) => {
+ const kwicUrl: string = configMC.backendBaseUrl + configMC.backendApiKwicPath;
+ this.helperService.makePostRequest(this.http, this.toastCtrl, kwicUrl, formData).then((svgString: string) => {
+ this.exerciseService.kwicGraphs = svgString;
+ this.helperService.goToKwicPage(this.navCtrl).then();
+ return resolve();
+ }, () => {
+ return reject();
});
- toast.present().then();
});
}
- initTranslation() {
- this.translateService.get('TEXT_TOO_LONG').subscribe(
- value => this.textTooLongString = value + configMC.maxTextLength);
- this.translateService.get('QUERY_VALUE_EMPTY').subscribe(value => this.emptyQueryValueString = value);
- }
-
ngOnInit(): void {
- this.corpusService.adjustTranslations();
+ this.corpusService.adjustTranslations().then();
}
}
diff --git a/src/app/exercise.service.spec.ts b/src/app/exercise.service.spec.ts
index 13d7ccd..96a4896 100644
--- a/src/app/exercise.service.spec.ts
+++ b/src/app/exercise.service.spec.ts
@@ -2,16 +2,53 @@ import {TestBed} from '@angular/core/testing';
import {ExerciseService} from './exercise.service';
import {APP_BASE_HREF} from '@angular/common';
+import configMC from '../configMC';
describe('ExerciseService', () => {
- beforeEach(() => TestBed.configureTestingModule({
- providers: [
- {provide: APP_BASE_HREF, useValue: '/'},
- ],
- }));
+ let exerciseService: ExerciseService;
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ {provide: APP_BASE_HREF, useValue: '/'},
+ ],
+ });
+ exerciseService = TestBed.inject(ExerciseService);
+ });
it('should be created', () => {
- const service: ExerciseService = TestBed.get(ExerciseService);
- expect(service).toBeTruthy();
+ expect(exerciseService).toBeTruthy();
+ });
+
+ it('should create a GUID', () => {
+ const guid: string = exerciseService.createGuid();
+ expect(guid.length).toBe(36);
+ });
+
+ it('should initialize H5P', (done) => {
+ let h5pCalled = false;
+ // tslint:disable-next-line:no-string-literal
+ window['H5P'] = {
+ jQuery: () => {
+ h5pCalled = true;
+ return {
+ empty: () => {
+ return {
+ h5p: (selector: string) => {
+ }
+ };
+ }
+ };
+ }, off: () => {
+ }
+ };
+ exerciseService.initH5P('').then(() => {
+ expect(h5pCalled).toBe(true);
+ // load script, restore old H5P variable
+ const script: HTMLScriptElement = document.createElement('script');
+ script.type = 'text/javascript';
+ script.src = configMC.h5pAssetFilePath;
+ document.querySelector('head').appendChild(script);
+ done();
+ });
});
});
diff --git a/src/app/exercise.service.ts b/src/app/exercise.service.ts
index a6cfca6..1e9369b 100644
--- a/src/app/exercise.service.ts
+++ b/src/app/exercise.service.ts
@@ -17,6 +17,7 @@ window.onresize = () => {
export class ExerciseService {
public excludeOOV = false;
public fillBlanksString = 'fill_blanks';
+ public h5pContainerString = '.h5p-container';
public h5pIframeString = '#h5p-iframe-1';
public kwicGraphs: string;
public vocListString = 'voc_list';
@@ -24,8 +25,8 @@ export class ExerciseService {
constructor() {
}
- createGuid() {
- function s4() {
+ createGuid(): string {
+ function s4(): string {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
@@ -35,14 +36,18 @@ export class ExerciseService {
s4() + '-' + s4() + s4() + s4();
}
- initH5P(exerciseTypePath: string) {
- // dirty hack to get H5P going without explicit button click on the new page
- setTimeout(() => {
- H5P.jQuery('.h5p-container').empty().h5p({
- frameJs: 'assets/dist/js/h5p-standalone-frame.min.js',
- frameCss: 'assets/dist/styles/h5p.css',
- h5pContent: 'assets/h5p/' + exerciseTypePath
- });
- }, 50);
+ initH5P(exerciseTypePath: string): Promise {
+ return new Promise(resolve => {
+ // dirty hack to get H5P going without explicit button click on the new page
+ setTimeout(() => {
+ // noinspection TypeScriptValidateJSTypes
+ H5P.jQuery(this.h5pContainerString).empty().h5p({
+ frameJs: 'assets/dist/js/h5p-standalone-frame.min.js',
+ frameCss: 'assets/dist/styles/h5p.css',
+ h5pContent: 'assets/h5p/' + exerciseTypePath
+ });
+ return resolve();
+ }, 50);
+ });
}
}
diff --git a/src/app/exercise/exercise.page.html b/src/app/exercise/exercise.page.html
index 4cb112c..11a55f4 100644
--- a/src/app/exercise/exercise.page.html
+++ b/src/app/exercise/exercise.page.html
@@ -4,8 +4,8 @@
{{ 'EXERCISE' | translate }}
-
-
+
+
diff --git a/src/app/exercise/exercise.page.spec.ts b/src/app/exercise/exercise.page.spec.ts
index 4f82b71..b429901 100644
--- a/src/app/exercise/exercise.page.spec.ts
+++ b/src/app/exercise/exercise.page.spec.ts
@@ -1,18 +1,25 @@
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
-
import {ExercisePage} from './exercise.page';
import {HttpClientModule} from '@angular/common/http';
import {IonicStorageModule} from '@ionic/storage';
-import {RouterModule} from '@angular/router';
+import {ActivatedRoute, RouterModule} from '@angular/router';
import {TranslateTestingModule} from '../translate-testing/translate-testing.module';
import {APP_BASE_HREF} from '@angular/common';
-import {CorpusService} from '../corpus.service';
-import {ToastController} from '@ionic/angular';
+import {of} from 'rxjs';
+import {AnnisResponse} from '../models/annisResponse';
+import {ExerciseType, MoodleExerciseType} from '../models/enum';
+import MockMC from '../models/mockMC';
+import Spy = jasmine.Spy;
+import configMC from '../../configMC';
describe('ExercisePage', () => {
- let component: ExercisePage;
+ let exercisePage: ExercisePage;
let fixture: ComponentFixture
;
+ let checkSpy: Spy;
+ const activatedRouteMock: any = {queryParams: of({eid: 'eid', type: ExerciseType.cloze.toString()})};
+ let getRequestSpy: Spy;
+ let h5pSpy: Spy;
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -25,19 +32,57 @@ describe('ExercisePage', () => {
],
providers: [
{provide: APP_BASE_HREF, useValue: '/'},
+ {provide: ActivatedRoute, useValue: activatedRouteMock}
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
- .compileComponents();
- }));
-
- beforeEach(() => {
+ .compileComponents().then();
fixture = TestBed.createComponent(ExercisePage);
- component = fixture.componentInstance;
+ exercisePage = fixture.componentInstance;
+ exercisePage.helperService.applicationState.next(exercisePage.helperService.deepCopy(MockMC.applicationState));
+ h5pSpy = spyOn(exercisePage.exerciseService, 'initH5P').and.returnValue(Promise.resolve());
+ checkSpy = spyOn(exercisePage.corpusService, 'checkAnnisResponse').and.returnValue(Promise.resolve());
+ getRequestSpy = spyOn(exercisePage.helperService, 'makeGetRequest').and.returnValue(Promise.resolve(
+ new AnnisResponse({exercise_type: MoodleExerciseType.cloze.toString()})));
fixture.detectChanges();
- });
+ }));
it('should create', () => {
- expect(component).toBeTruthy();
+ expect(exercisePage).toBeTruthy();
+ });
+
+ it('should be initialized', (done) => {
+ const loadExerciseSpy: Spy = spyOn(exercisePage, 'loadExercise');
+ checkSpy.and.callFake(() => Promise.reject());
+ exercisePage.ngOnInit().then(() => {
+ expect(loadExerciseSpy).toHaveBeenCalledTimes(0);
+ done();
+ });
+ });
+
+ it('should load the exercise', (done) => {
+ exercisePage.loadExercise().then(() => {
+ expect(exercisePage.corpusService.exercise.type).toBe(ExerciseType.cloze);
+ getRequestSpy.and.returnValue(Promise.resolve(new AnnisResponse({exercise_type: MoodleExerciseType.markWords.toString()})));
+ exercisePage.loadExercise().then(() => {
+ expect(h5pSpy).toHaveBeenCalledWith(configMC.excerciseTypePathMarkWords);
+ getRequestSpy.and.callFake(() => Promise.reject());
+ exercisePage.loadExercise().then(() => {
+ }, () => {
+ activatedRouteMock.queryParams = of({eid: '', type: ExerciseType.matching.toString()});
+ exercisePage.loadExercise().then(() => {
+ expect(h5pSpy).toHaveBeenCalledWith(ExerciseType.matching.toString());
+ activatedRouteMock.queryParams = of({
+ eid: '',
+ type: exercisePage.exerciseService.vocListString
+ });
+ exercisePage.loadExercise().then(() => {
+ expect(h5pSpy).toHaveBeenCalledWith(exercisePage.exerciseService.fillBlanksString);
+ done();
+ });
+ });
+ });
+ });
+ });
});
});
diff --git a/src/app/exercise/exercise.page.ts b/src/app/exercise/exercise.page.ts
index d3273cd..141d4dd 100644
--- a/src/app/exercise/exercise.page.ts
+++ b/src/app/exercise/exercise.page.ts
@@ -21,8 +21,6 @@ import {Storage} from '@ionic/storage';
})
export class ExercisePage implements OnInit {
- HelperService = HelperService;
-
constructor(public navCtrl: NavController,
public activatedRoute: ActivatedRoute,
public translateService: TranslateService,
@@ -32,49 +30,61 @@ export class ExercisePage implements OnInit {
public helperService: HelperService,
public corpusService: CorpusService,
public storage: Storage) {
- this.corpusService.checkAnnisResponse().then(() => {
- this.loadExercise();
- }, () => {
- });
}
- loadExercise(): void {
- this.activatedRoute.queryParams.subscribe((params: object) => {
- if (params['eid']) {
- let url: string = configMC.backendBaseUrl + configMC.backendApiExercisePath;
- const httpParams: HttpParams = new HttpParams().set('eid', params['eid']);
- HelperService.makeGetRequest(this.http, this.toastCtrl, url, httpParams).then((ar: AnnisResponse) => {
- HelperService.applicationState.pipe(take(1)).subscribe((as: ApplicationState) => {
- as.mostRecentSetup.annisResponse = ar;
- this.helperService.saveApplicationState(as).then();
- this.corpusService.annisResponse = ar;
- const met: MoodleExerciseType = MoodleExerciseType[ar.exercise_type];
- this.corpusService.exercise.type = ExerciseType[met.toString()];
- // this will be called via GET request from the h5p standalone javascript library
- url = `${configMC.backendBaseUrl}${configMC.backendApiH5pPath}` +
- `?eid=${this.corpusService.annisResponse.exercise_id}&lang=${this.translateService.currentLang}`;
- this.storage.set(configMC.localStorageKeyH5P, url).then();
- const exerciseTypePath: string = this.corpusService.exercise.type === ExerciseType.markWords ?
- 'mark_words' : 'drag_text';
- this.exerciseService.initH5P(exerciseTypePath);
+ loadExercise(): Promise {
+ return new Promise((resolve, reject) => {
+ this.activatedRoute.queryParams.subscribe((params: object) => {
+ if (params['eid']) {
+ let url: string = configMC.backendBaseUrl + configMC.backendApiExercisePath;
+ const httpParams: HttpParams = new HttpParams().set('eid', params['eid']);
+ this.helperService.makeGetRequest(this.http, this.toastCtrl, url, httpParams).then((ar: AnnisResponse) => {
+ this.helperService.applicationState.pipe(take(1)).subscribe((as: ApplicationState) => {
+ as.mostRecentSetup.annisResponse = ar;
+ this.helperService.saveApplicationState(as).then();
+ this.corpusService.annisResponse = ar;
+ const met: MoodleExerciseType = MoodleExerciseType[ar.exercise_type];
+ this.corpusService.exercise.type = ExerciseType[met.toString()];
+ // this will be called via GET request from the h5p standalone javascript library
+ url = `${configMC.backendBaseUrl}${configMC.backendApiH5pPath}` +
+ `?eid=${this.corpusService.annisResponse.exercise_id}&lang=${this.translateService.currentLang}`;
+ this.storage.set(configMC.localStorageKeyH5P, url).then();
+ const exerciseTypePath: string = this.corpusService.exercise.type === ExerciseType.markWords ?
+ configMC.excerciseTypePathMarkWords : 'drag_text';
+ this.exerciseService.initH5P(exerciseTypePath).then(() => {
+ return resolve();
+ });
+ });
+ }, () => {
+ return reject();
});
- }, () => {
- });
- } else {
- const exerciseType: string = params['type'];
- const exerciseTypePath: string = exerciseType === this.exerciseService.vocListString ?
- this.exerciseService.fillBlanksString : exerciseType;
- const file: string = params['file'];
- const lang: string = this.translateService.currentLang;
- this.storage.set(configMC.localStorageKeyH5P,
- HelperService.baseUrl + '/assets/h5p/' + exerciseType + '/content/' + file + '_' + lang + '.json')
- .then();
- this.exerciseService.initH5P(exerciseTypePath);
- }
+ } else {
+ const exerciseType: string = params['type'];
+ const exerciseTypePath: string = exerciseType === this.exerciseService.vocListString ?
+ this.exerciseService.fillBlanksString : exerciseType;
+ const file: string = params['file'];
+ const lang: string = this.translateService.currentLang;
+ this.storage.set(configMC.localStorageKeyH5P,
+ this.helperService.baseUrl + '/assets/h5p/' + exerciseType + '/content/' + file + '_' + lang + '.json')
+ .then();
+ this.exerciseService.initH5P(exerciseTypePath).then(() => {
+ return resolve();
+ });
+ }
+ });
});
}
- ngOnInit() {
+ ngOnInit(): Promise {
+ return new Promise(resolve => {
+ this.corpusService.checkAnnisResponse().then(() => {
+ this.loadExercise().then(() => {
+ return resolve();
+ });
+ }, () => {
+ return resolve();
+ });
+ });
}
}
diff --git a/src/app/helper.service.spec.ts b/src/app/helper.service.spec.ts
index ef3bc23..dfd162b 100644
--- a/src/app/helper.service.spec.ts
+++ b/src/app/helper.service.spec.ts
@@ -4,21 +4,205 @@ import {HelperService} from './helper.service';
import {IonicStorageModule} from '@ionic/storage';
import {TranslateTestingModule} from './translate-testing/translate-testing.module';
import {HttpClientTestingModule} from '@angular/common/http/testing';
+import {TranslateHttpLoader} from '@ngx-translate/http-loader';
+import {Observable, of, range} from 'rxjs';
+import Spy = jasmine.Spy;
+import {PartOfSpeechValue} from './models/enum';
+import {NavController, ToastController} from '@ionic/angular';
+import configMC from '../configMC';
+import {AppRoutingModule} from './app-routing.module';
+import {APP_BASE_HREF} from '@angular/common';
+import {ApplicationState} from './models/applicationState';
+import {take} from 'rxjs/operators';
+import {HttpErrorResponse, HttpParams} from '@angular/common/http';
+import MockMC from './models/mockMC';
describe('HelperService', () => {
+ let helperService: HelperService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
IonicStorageModule.forRoot(),
TranslateTestingModule,
+ AppRoutingModule,
+ ],
+ providers: [
+ {provide: APP_BASE_HREF, useValue: '/'},
],
- providers: [],
});
+ helperService = TestBed.inject(HelperService);
});
it('should be created', () => {
- const service: HelperService = TestBed.get(HelperService);
- expect(service).toBeTruthy();
+ expect(helperService).toBeTruthy();
+ });
+
+ it('should test IE11 mode', () => {
+ // @ts-ignore
+ // tslint:disable-next-line:no-string-literal
+ window['MSInputMethodContext'] = true;
+ // tslint:disable-next-line:no-string-literal
+ document['documentMode'] = true;
+ const helperService2: HelperService = new HelperService(helperService.http, helperService.storage, helperService.translate);
+ expect(helperService2.isIE11).toBe(true);
+ });
+
+ it('should create a translate loader', () => {
+ const thl: TranslateHttpLoader = HelperService.createTranslateLoader(helperService.http);
+ expect(thl.suffix).toBe('.json');
+ });
+
+ it('should shuffle an array', () => {
+ const array: number[] = [0, 1];
+ const firstNumberArray: number[] = [];
+ range(0, 100).forEach(() => {
+ firstNumberArray.push(HelperService.shuffle(array)[0]);
+ });
+ expect(firstNumberArray).toContain(1);
+ });
+
+ it('should create a deep copy', () => {
+ expect(helperService.deepCopy(undefined)).toBe(undefined);
+ const oldDate: Date = new Date(1);
+ const newDate: Date = helperService.deepCopy(oldDate);
+ newDate.setTime(10000);
+ expect(oldDate.getTime()).toBe(1);
+ const oldArray: number[] = [0];
+ const newArray: number[] = helperService.deepCopy(oldArray);
+ newArray[0] = 1;
+ expect(oldArray[0]).toBe(0);
+ const oldObject: any = {unchanged: true};
+ const newObject: any = helperService.deepCopy(oldObject);
+ newObject.unchanged = false;
+ expect(oldObject.unchanged).toBe(true);
+ });
+
+ it('should get a delayed translation', (done) => {
+ const key = 'TEST';
+ let translateSpy: Spy;
+ helperService.getDelayedTranslation(helperService.translate, key).then((value: string) => {
+ expect(value).toBe(key);
+ translateSpy.and.returnValue(of(key.toLowerCase()));
+ helperService.getDelayedTranslation(helperService.translate, key).then((newValue: string) => {
+ expect(newValue).toBe(key.toLowerCase());
+ done();
+ });
+ });
+ translateSpy = spyOn(helperService.translate, 'get').and.returnValue(of(key));
+ });
+
+ it('should get enum values', () => {
+ const enumValues: string[] = helperService.getEnumValues(PartOfSpeechValue);
+ enumValues.forEach((ev: string) => expect(PartOfSpeechValue[ev]).toBeTruthy());
+ expect(enumValues.length).toBe(7);
+ });
+
+ it('should go to a specific page', (done) => {
+ function checkNavigation(navFunctions: any[], pageUrls: string[], navController: NavController, navSpy: Spy): Promise {
+ return new Promise(resolve => {
+ range(0, navFunctions.length).forEach(async (idx: number) => {
+ await navFunctions[idx](navController);
+ expect(navSpy).toHaveBeenCalledWith(pageUrls[idx]);
+ });
+ return resolve();
+ });
+ }
+
+ const navCtrl: NavController = TestBed.inject(NavController);
+ const forwardSpy: Spy = spyOn(navCtrl, 'navigateForward').and.returnValue(Promise.resolve(true));
+ const navFnArr: any[] = [helperService.goToAuthorDetailPage, helperService.goToDocExercisesPage, helperService.goToDocSoftwarePage,
+ helperService.goToDocVocUnitPage, helperService.goToExerciseListPage, helperService.goToExerciseParametersPage,
+ helperService.goToImprintPage, helperService.goToInfoPage, helperService.goToPreviewPage, helperService.goToSourcesPage,
+ helperService.goToTextRangePage, helperService.goToVocabularyCheckPage, helperService.goToKwicPage];
+ const pageUrlArr: string[] = [configMC.pageUrlAuthorDetail, configMC.pageUrlDocExercises, configMC.pageUrlDocSoftware,
+ configMC.pageUrlDocVocUnit, configMC.pageUrlExerciseList, configMC.pageUrlExerciseParameters, configMC.pageUrlImprint,
+ configMC.pageUrlInfo, configMC.pageUrlPreview, configMC.pageUrlSources, configMC.pageUrlTextRange,
+ configMC.pageUrlVocabularyCheck, configMC.pageUrlKwic];
+ checkNavigation(navFnArr, pageUrlArr, navCtrl, forwardSpy).then(() => {
+ helperService.goToAuthorPage(navCtrl).then(() => {
+ expect(helperService.isVocabularyCheck).toBeFalsy();
+ helperService.goToShowTextPage(navCtrl, true).then(() => {
+ expect(helperService.isVocabularyCheck).toBe(true);
+ const rootSpy: Spy = spyOn(navCtrl, 'navigateRoot').and.returnValue(Promise.resolve(true));
+ helperService.goToHomePage(navCtrl).then(() => {
+ expect(rootSpy).toHaveBeenCalledWith(configMC.pageUrlHome);
+ helperService.goToTestPage(navCtrl).then(() => {
+ expect(rootSpy).toHaveBeenCalledWith(configMC.pageUrlTest);
+ done();
+ });
+ });
+ });
+ });
+ });
+ });
+
+ it('should initialize the application state', (done) => {
+ function updateState(newState: ApplicationState): Promise {
+ return new Promise(resolve => {
+ helperService.applicationStateCache = null;
+ helperService.initApplicationState();
+ helperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
+ resolve(state);
+ });
+ });
+ }
+
+ const localStorageSpy: Spy = spyOn(helperService.storage, 'get').withArgs(configMC.localStorageKeyApplicationState)
+ .and.returnValue(Promise.resolve(JSON.stringify(new ApplicationState())));
+ updateState(new ApplicationState()).then((state: ApplicationState) => {
+ expect(state).toBeTruthy();
+ localStorageSpy.and.returnValue(Promise.resolve(new ApplicationState({exerciseList: []})));
+ updateState(new ApplicationState({exerciseList: []})).then((state2: ApplicationState) => {
+ expect(state2.exerciseList.length).toBe(0);
+ helperService.initApplicationState();
+ helperService.applicationState.pipe(take(1)).subscribe((state3: ApplicationState) => {
+ expect(state3.exerciseList.length).toBe(0);
+ done();
+ });
+ });
+ });
+ });
+
+ it('should make a get request', (done) => {
+ const toastCtrl: ToastController = TestBed.inject(ToastController);
+ spyOn(toastCtrl, 'create').and.returnValue(Promise.resolve({present: () => Promise.resolve()} as HTMLIonToastElement));
+ const httpSpy: Spy = spyOn(helperService.http, 'get').and.returnValue(of(0));
+ helperService.makeGetRequest(helperService.http, toastCtrl, '', new HttpParams()).then((result: number) => {
+ expect(httpSpy).toHaveBeenCalledTimes(1);
+ expect(result).toBe(0);
+ httpSpy.and.returnValue(new Observable(subscriber => subscriber.error(new HttpErrorResponse({status: 500}))));
+ helperService.makeGetRequest(helperService.http, toastCtrl, '', new HttpParams()).then(() => {
+ }, (error: HttpErrorResponse) => {
+ expect(error.status).toBe(500);
+ done();
+ });
+ });
+ });
+
+ it('should make a post request', (done) => {
+ const toastCtrl: ToastController = TestBed.inject(ToastController);
+ spyOn(toastCtrl, 'create').and.returnValue(Promise.resolve({present: () => Promise.resolve()} as HTMLIonToastElement));
+ const httpSpy: Spy = spyOn(helperService.http, 'post').and.returnValue(of(0));
+ helperService.makePostRequest(helperService.http, toastCtrl, '', new FormData()).then((result: number) => {
+ expect(httpSpy).toHaveBeenCalledTimes(1);
+ expect(result).toBe(0);
+ httpSpy.and.returnValue(new Observable(subscriber => subscriber.error(new HttpErrorResponse({status: 500}))));
+ helperService.makePostRequest(helperService.http, toastCtrl, '', new FormData()).then(() => {
+ }, (error: HttpErrorResponse) => {
+ expect(error.status).toBe(500);
+ done();
+ });
+ });
+ });
+
+ it('should save the application state', (done) => {
+ helperService.saveApplicationState(helperService.deepCopy(MockMC.applicationState)).then(() => {
+ helperService.storage.get(configMC.localStorageKeyApplicationState).then((jsonString: string) => {
+ const state: ApplicationState = JSON.parse(jsonString) as ApplicationState;
+ expect(state.mostRecentSetup.annisResponse.nodes.length).toBe(1);
+ done();
+ });
+ });
});
});
diff --git a/src/app/helper.service.ts b/src/app/helper.service.ts
index f4db2c3..43c4e57 100644
--- a/src/app/helper.service.ts
+++ b/src/app/helper.service.ts
@@ -16,12 +16,12 @@ import configMC from '../configMC';
providedIn: 'root'
})
export class HelperService {
-
- public static applicationState: ReplaySubject = null;
- private static applicationStateCache: ApplicationState = null;
- public static baseUrl: string = location.protocol.concat('//').concat(window.location.host) +
+ public static generalErrorAlertMessage: string;
+ public applicationState: ReplaySubject = null;
+ public applicationStateCache: ApplicationState = null;
+ public baseUrl: string = location.protocol.concat('//').concat(window.location.host) +
window.location.pathname.split('/').slice(0, -1).join('/');
- public static caseMap: { [rawValue: string]: CaseValue } = {
+ public caseMap: { [rawValue: string]: CaseValue } = {
Nom: CaseValue.nominative,
Gen: CaseValue.genitive,
Dat: CaseValue.dative,
@@ -30,11 +30,11 @@ export class HelperService {
Voc: CaseValue.vocative,
Loc: CaseValue.locative,
};
- public static corpusUpdateCompletedString: string;
- public static currentError: HttpErrorResponse;
- public static currentLanguage: Language;
- public static currentPopover: any;
- public static dependencyMap: { [rawValue: string]: DependencyValue } = {
+ public corpusUpdateCompletedString: string;
+ public currentError: HttpErrorResponse;
+ public currentLanguage: Language;
+ public currentPopover: HTMLIonPopoverElement;
+ public dependencyMap: { [rawValue: string]: DependencyValue } = {
acl: DependencyValue.adjectivalClause,
advcl: DependencyValue.adverbialClauseModifier,
advmod: DependencyValue.adverbialModifier,
@@ -75,16 +75,14 @@ export class HelperService {
vocative: DependencyValue.vocative,
xcomp: DependencyValue.clausalComplement,
};
- public static generalErrorAlertMessage: string;
- public static isIE11: boolean = !!(window as any).MSInputMethodContext && !!(document as any).documentMode;
- public static isLoading = false;
- public static isDevMode = ['localhost'].indexOf(window.location.hostname) > -1; // set this to "false" for simulated production mode
- public static isVocabularyCheck = false;
- public static languages: Language[] = [new Language({
- name: 'English',
- shortcut: 'en'
- }), new Language({name: 'Deutsch', shortcut: 'de'})];
- public static partOfSpeechMap: { [rawValue: string]: PartOfSpeechValue } = {
+ public isIE11: boolean = !!(window as any).MSInputMethodContext && !!(document as any).documentMode;
+ public isDevMode = ['localhost'].indexOf(window.location.hostname) > -1; // set this to "false" for simulated production mode
+ public isVocabularyCheck = false;
+ public languages: Language[] = [
+ new Language({name: 'English', shortcut: 'en'}),
+ new Language({name: 'Deutsch', shortcut: 'de'})];
+ public openRequests: string[] = [];
+ public partOfSpeechMap: { [rawValue: string]: PartOfSpeechValue } = {
ADJ: PartOfSpeechValue.adjective,
ADP: PartOfSpeechValue.preposition,
ADV: PartOfSpeechValue.adverb,
@@ -105,19 +103,67 @@ export class HelperService {
};
constructor(public http: HttpClient,
- private storage: Storage,
+ public storage: Storage,
public translate: TranslateService,
) {
this.initConfig();
this.initLanguage();
+ this.initApplicationState();
}
// The translate loader needs to know where to load i18n files in Ionic's static asset pipeline.
- static createTranslateLoader(http: HttpClient) {
+ static createTranslateLoader(http: HttpClient): TranslateHttpLoader {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
- static delayedTranslation(translate: TranslateService, key: string) {
+ /**
+ * Shuffles array in place.
+ * @param array items An array containing the items.
+ */
+ static shuffle(array: Array): Array {
+ let j, x, i;
+ for (i = array.length - 1; i > 0; i--) {
+ j = Math.floor(Math.random() * (i + 1));
+ x = array[i];
+ array[i] = array[j];
+ array[j] = x;
+ }
+ return array;
+ }
+
+ deepCopy(obj: object): any {
+ let copy;
+ // Handle the 3 simple types, and null or undefined
+ if (null === obj || 'object' !== typeof obj) {
+ return obj;
+ }
+ // Handle Date
+ if (obj instanceof Date) {
+ copy = new Date();
+ copy.setTime(obj.getTime());
+ return copy;
+ }
+ // Handle Array
+ if (obj instanceof Array) {
+ copy = [];
+ for (let i = 0, len = obj.length; i < len; i++) {
+ copy[i] = this.deepCopy(obj[i]);
+ }
+ return copy;
+ }
+ // Handle Object
+ if (obj instanceof Object) {
+ copy = {};
+ for (const attr in obj) {
+ if (obj.hasOwnProperty(attr)) {
+ copy[attr] = this.deepCopy(obj[attr]);
+ }
+ }
+ return copy;
+ }
+ }
+
+ getDelayedTranslation(translate: TranslateService, key: string) {
return new Promise(resolve => {
translate.get(key).subscribe((value: string) => {
// check if we got the correct translated value
@@ -132,165 +178,117 @@ export class HelperService {
});
}
- static getEnumValues(target: any): string[] {
+ getEnumValues(target: any): string[] {
return Object.keys(target).filter((value, index, array) => {
return index % 2 !== 0;
});
}
- static goToAuthorPage(navCtrl: NavController): Promise {
- HelperService.isVocabularyCheck = false;
- return navCtrl.navigateForward('/author');
+ goToAuthorDetailPage(navCtrl: NavController): Promise {
+ return navCtrl.navigateForward(configMC.pageUrlAuthorDetail);
}
- static goToAuthorDetailPage(navCtrl: NavController): Promise {
- return navCtrl.navigateForward('/author-detail');
+ goToAuthorPage(navCtrl: NavController): Promise {
+ this.isVocabularyCheck = false;
+ return navCtrl.navigateForward(configMC.pageUrlAuthor);
}
- static goToDocExercisesPage(navCtrl: NavController): Promise {
- return navCtrl.navigateForward('/doc-exercises');
+ goToDocExercisesPage(navCtrl: NavController): Promise {
+ return navCtrl.navigateForward(configMC.pageUrlDocExercises);
}
- static goToDocSoftwarePage(navCtrl: NavController): Promise {
- return navCtrl.navigateForward('/doc-software');
+ goToDocSoftwarePage(navCtrl: NavController): Promise {
+ return navCtrl.navigateForward(configMC.pageUrlDocSoftware);
}
- static goToDocVocUnitPage(navCtrl: NavController): Promise {
- return navCtrl.navigateForward('/doc-voc-unit');
+ goToDocVocUnitPage(navCtrl: NavController): Promise {
+ return navCtrl.navigateForward(configMC.pageUrlDocVocUnit);
}
- static goToExerciseListPage(navCtrl: NavController): Promise {
- return navCtrl.navigateForward('/exercise-list');
+ goToExerciseListPage(navCtrl: NavController): Promise {
+ return navCtrl.navigateForward(configMC.pageUrlExerciseList);
}
- static goToExerciseParametersPage(navCtrl: NavController): Promise {
- return navCtrl.navigateForward('exercise-parameters');
+ goToExerciseParametersPage(navCtrl: NavController): Promise {
+ return navCtrl.navigateForward(configMC.pageUrlExerciseParameters);
}
- static goToHomePage(navCtrl: NavController): Promise {
- return navCtrl.navigateRoot('/home');
+ goToHomePage(navCtrl: NavController): Promise {
+ return navCtrl.navigateRoot(configMC.pageUrlHome);
}
- static goToImprintPage(navCtrl: NavController): Promise {
- return navCtrl.navigateForward('/imprint');
+ goToImprintPage(navCtrl: NavController): Promise {
+ return navCtrl.navigateForward(configMC.pageUrlImprint);
}
- static goToInfoPage(navCtrl: NavController): Promise {
- return navCtrl.navigateForward('/info');
+ goToInfoPage(navCtrl: NavController): Promise {
+ return navCtrl.navigateForward(configMC.pageUrlInfo);
}
- static goToPreviewPage(navCtrl: NavController): Promise {
- return navCtrl.navigateForward('preview');
+ goToKwicPage(navCtrl: NavController): Promise {
+ return navCtrl.navigateForward(configMC.pageUrlKwic);
}
- static goToShowTextPage(navCtrl: NavController, isVocabularyCheck: boolean = false): Promise {
- return new Promise((resolve) => {
- navCtrl.navigateForward('/show-text').then((result: boolean) => {
- HelperService.isVocabularyCheck = isVocabularyCheck;
- return resolve(result);
- });
- });
+ goToPreviewPage(navCtrl: NavController): Promise {
+ return navCtrl.navigateForward(configMC.pageUrlPreview);
}
- static goToSourcesPage(navCtrl: NavController): Promise {
- return navCtrl.navigateForward('/sources');
+ goToSemanticsPage(navCtrl: NavController): Promise {
+ return navCtrl.navigateForward(configMC.pageUrlSemantics);
}
- static goToTestPage(navCtrl: NavController): Promise {
- return navCtrl.navigateRoot('/test');
- }
-
- static goToTextRangePage(navCtrl: NavController): Promise {
- return navCtrl.navigateForward('/text-range');
- }
-
- static goToVocabularyCheckPage(navCtrl: NavController): Promise {
+ goToShowTextPage(navCtrl: NavController, isVocabularyCheck: boolean = false): Promise {
return new Promise((resolve) => {
- navCtrl.navigateForward('/vocabulary-check').then((result: boolean) => {
+ navCtrl.navigateForward(configMC.pageUrlShowText).then((result: boolean) => {
+ this.isVocabularyCheck = isVocabularyCheck;
return resolve(result);
});
});
}
- static loadTranslations(translate: TranslateService) {
- // dirty hack to wait until the translation loader is initialized in IE11
- HelperService.delayedTranslation(translate, 'CORPUS_UPDATE_COMPLETED').then((value: string) => {
- HelperService.corpusUpdateCompletedString = value;
- });
- HelperService.delayedTranslation(translate, 'ERROR_GENERAL_ALERT').then((value: string) => {
- HelperService.generalErrorAlertMessage = value;
- });
+ goToSourcesPage(navCtrl: NavController): Promise {
+ return navCtrl.navigateForward(configMC.pageUrlSources);
}
- static makeGetRequest(http: HttpClient, toastCtrl: ToastController, url: string, params: HttpParams,
- errorMessage: string = HelperService.generalErrorAlertMessage): Promise {
- HelperService.currentError = null;
- // dirty hack to avoid ExpressionChangedAfterItHasBeenCheckedError
- setTimeout(() => {
- HelperService.isLoading = true;
- }, 0);
- return new Promise(((resolve, reject) => {
- http.get(url, {params}).subscribe((result: any) => {
- // dirty hack to avoid ExpressionChangedAfterItHasBeenCheckedError
- setTimeout(() => {
- HelperService.isLoading = false;
- }, 0);
- return resolve(result);
- }, async (error: HttpErrorResponse) => {
- // dirty hack to avoid ExpressionChangedAfterItHasBeenCheckedError
- setTimeout(() => {
- HelperService.isLoading = false;
- }, 0);
- HelperService.currentError = error;
- const toast: HTMLIonToastElement = await toastCtrl.create({
- message: errorMessage,
- duration: 3000,
- position: 'top'
- }).catch() as HTMLIonToastElement;
- toast.present().then(() => {
- }, () => {
- });
- return reject(error);
- });
- }));
+ goToTestPage(navCtrl: NavController): Promise {
+ return navCtrl.navigateRoot(configMC.pageUrlTest);
}
- /**
- * Shuffles array in place.
- * @param array items An array containing the items.
- */
- static shuffle(array: Array) {
- let j, x, i;
- for (i = array.length - 1; i > 0; i--) {
- j = Math.floor(Math.random() * (i + 1));
- x = array[i];
- array[i] = array[j];
- array[j] = x;
- }
- return array;
+ goToTextRangePage(navCtrl: NavController): Promise {
+ return navCtrl.navigateForward(configMC.pageUrlTextRange);
+ }
+
+ goToVocabularyCheckPage(navCtrl: NavController): Promise {
+ return navCtrl.navigateForward(configMC.pageUrlVocabularyCheck);
+ }
+
+ handleRequestError(toastCtrl: ToastController, error: HttpErrorResponse, errorMessage: string, url: string): void {
+ this.openRequests.splice(this.openRequests.indexOf(url), 1);
+ this.currentError = error;
+ this.showToast(toastCtrl, errorMessage).then();
}
initApplicationState(): void {
- HelperService.applicationState = new ReplaySubject(1);
- if (!HelperService.applicationStateCache) {
+ this.applicationState = new ReplaySubject(1);
+ if (!this.applicationStateCache) {
this.storage.get(configMC.localStorageKeyApplicationState).then((jsonString: string) => {
- HelperService.applicationStateCache = new ApplicationState({
+ this.applicationStateCache = new ApplicationState({
currentSetup: new TextData(),
exerciseList: []
});
if (jsonString) {
const state: ApplicationState = JSON.parse(jsonString) as ApplicationState;
state.exerciseList = state.exerciseList ? state.exerciseList : [];
- HelperService.applicationStateCache = state;
+ this.applicationStateCache = state;
}
- HelperService.applicationState.next(HelperService.applicationStateCache);
+ this.applicationState.next(this.applicationStateCache);
});
} else {
- HelperService.applicationState.next(HelperService.applicationStateCache);
+ this.applicationState.next(this.applicationStateCache);
}
}
- initConfig() {
+ initConfig(): void {
if (!configMC.backendBaseUrl) {
const part1: string = location.protocol.concat('//').concat(window.location.host);
configMC.backendBaseUrl = part1.concat(configMC.backendBaseApiPath).concat('/');
@@ -300,18 +298,66 @@ export class HelperService {
initLanguage(): void {
// dirty hack to wait for the translateService intializing
setTimeout(() => {
- HelperService.currentLanguage = HelperService.languages.find(x => x.shortcut === this.translate.currentLang);
- HelperService.loadTranslations(this.translate);
+ this.currentLanguage = this.languages.find(x => x.shortcut === this.translate.currentLang);
+ this.loadTranslations(this.translate);
});
}
- saveApplicationState(mrs: ApplicationState) {
+ loadTranslations(translate: TranslateService): void {
+ // dirty hack to wait until the translation loader is initialized in IE11
+ this.getDelayedTranslation(translate, 'CORPUS_UPDATE_COMPLETED').then((value: string) => {
+ this.corpusUpdateCompletedString = value;
+ });
+ this.getDelayedTranslation(translate, 'ERROR_GENERAL_ALERT').then((value: string) => {
+ HelperService.generalErrorAlertMessage = value;
+ });
+ }
+
+ makeGetRequest(http: HttpClient, toastCtrl: ToastController, url: string, params: HttpParams,
+ errorMessage: string = HelperService.generalErrorAlertMessage): Promise {
+ return new Promise(((resolve, reject) => {
+ this.currentError = null;
+ this.openRequests.push(url);
+ http.get(url, {params}).subscribe((result: any) => {
+ this.openRequests.splice(this.openRequests.indexOf(url), 1);
+ return resolve(result);
+ }, async (error: HttpErrorResponse) => {
+ this.handleRequestError(toastCtrl, error, errorMessage, url);
+ return reject(error);
+ });
+ }));
+ }
+
+ makePostRequest(http: HttpClient, toastCtrl: ToastController, url: string, formData: FormData,
+ errorMessage: string = HelperService.generalErrorAlertMessage): Promise {
+ return new Promise(((resolve, reject) => {
+ this.currentError = null;
+ this.openRequests.push(url);
+ http.post(url, formData).subscribe((result: any) => {
+ this.openRequests.splice(this.openRequests.indexOf(url), 1);
+ return resolve(result);
+ }, async (error: HttpErrorResponse) => {
+ this.handleRequestError(toastCtrl, error, errorMessage, url);
+ return reject(error);
+ });
+ }));
+ }
+
+ saveApplicationState(state: ApplicationState): Promise {
return new Promise((resolve) => {
- HelperService.applicationStateCache = mrs;
- HelperService.applicationState.next(HelperService.applicationStateCache);
- this.storage.set(configMC.localStorageKeyApplicationState, JSON.stringify(mrs)).then(() => {
+ this.applicationStateCache = state;
+ this.applicationState.next(this.applicationStateCache);
+ this.storage.set(configMC.localStorageKeyApplicationState, JSON.stringify(state)).then(() => {
return resolve();
});
});
}
+
+ showToast(toastCtrl: ToastController, message: string, position: any = 'top'): Promise {
+ return toastCtrl.create({
+ message,
+ duration: 3000,
+ position
+ }).then((toast: HTMLIonToastElement) => toast.present());
+ }
}
diff --git a/src/app/home/home.page.html b/src/app/home/home.page.html
index 44ae67f..2e6c3de 100644
--- a/src/app/home/home.page.html
+++ b/src/app/home/home.page.html
@@ -10,10 +10,10 @@
-
-
-
+
+
+
{{lang.name}}
@@ -46,7 +46,7 @@
{{'EXERCISE_PARAMETERS' | translate }}
- {{ 'CONTINUE' | translate }}
+ {{ 'CONTINUE' | translate }}
@@ -62,7 +62,7 @@
{{'EXERCISE_TYPE_MATCHING' | translate }}
- {{ 'CONTINUE' | translate }}
+ {{ 'CONTINUE' | translate }}
@@ -79,7 +79,7 @@
{{'UNIT_EVALUATION_TITLE' | translate }}
- {{ 'CONTINUE' | translate }}
+ {{ 'CONTINUE' | translate }}
@@ -96,7 +96,7 @@
{{'DOC_VOC_UNIT' | translate }}
- {{ 'CONTINUE' | translate }}
+ {{ 'CONTINUE' | translate }}
@@ -113,7 +113,7 @@
-
+
{{ 'IMPRINT' | translate }}
diff --git a/src/app/home/home.page.spec.ts b/src/app/home/home.page.spec.ts
index a71e539..8d35cc9 100644
--- a/src/app/home/home.page.spec.ts
+++ b/src/app/home/home.page.spec.ts
@@ -7,9 +7,12 @@ import {IonicStorageModule} from '@ionic/storage';
import {RouterModule} from '@angular/router';
import {TranslateTestingModule} from '../translate-testing/translate-testing.module';
import {APP_BASE_HREF} from '@angular/common';
+import Spy = jasmine.Spy;
+import {ToastController} from '@ionic/angular';
+import MockMC from '../models/mockMC';
describe('HomePage', () => {
- let component: HomePage;
+ let homePage: HomePage;
let fixture: ComponentFixture;
beforeEach(async(() => {
@@ -23,19 +26,57 @@ describe('HomePage', () => {
],
providers: [
{provide: APP_BASE_HREF, useValue: '/'},
+ {provide: ToastController, useValue: MockMC.toastController}
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
- .compileComponents();
+ .compileComponents().then();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HomePage);
- component = fixture.componentInstance;
+ homePage = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
- expect(component).toBeTruthy();
+ expect(homePage).toBeTruthy();
+ });
+
+ it('should change the language', (done) => {
+ const translateSpy: Spy = spyOn(homePage.corpusService, 'adjustTranslations').and.returnValue(Promise.resolve());
+ spyOn(homePage.corpusService, 'processAnnisResponse');
+ homePage.changeLanguage('').then(() => {
+ expect(translateSpy).toHaveBeenCalledTimes(1);
+ homePage.changeLanguage('').then(() => {
+ expect(translateSpy).toHaveBeenCalledTimes(1);
+ done();
+ });
+ });
+ });
+
+ it('should be initialized', () => {
+ const translateSpy: Spy = spyOn(homePage.helperService, 'loadTranslations');
+ homePage.ionViewDidEnter();
+ expect(translateSpy).toHaveBeenCalledTimes(1);
+ homePage.helperService.isIE11 = true;
+ homePage.ngOnInit();
+ const tabs: HTMLElement = document.querySelector('#tabs') as HTMLElement;
+ expect(tabs.style.maxWidth).toBe('65%');
+ });
+
+ it('should refresh the corpora', (done) => {
+ homePage.isCorpusUpdateInProgress = true;
+ const getCorporaSpy: Spy = spyOn(homePage.corpusService, 'getCorpora').and.returnValue(Promise.resolve());
+ homePage.refreshCorpora().then(() => {
+ expect(homePage.isCorpusUpdateInProgress).toBe(false);
+ homePage.isCorpusUpdateInProgress = true;
+ getCorporaSpy.and.callFake(() => Promise.reject());
+ homePage.refreshCorpora().then(() => {
+ }, () => {
+ expect(homePage.isCorpusUpdateInProgress).toBe(false);
+ done();
+ });
+ });
});
});
diff --git a/src/app/home/home.page.ts b/src/app/home/home.page.ts
index 6227dec..ccf958f 100644
--- a/src/app/home/home.page.ts
+++ b/src/app/home/home.page.ts
@@ -1,11 +1,12 @@
/* tslint:disable:no-string-literal */
-import {ChangeDetectorRef, Component, OnInit} from '@angular/core';
+import {Component, OnInit} from '@angular/core';
import {HelperService} from 'src/app/helper.service';
import {NavController, ToastController} from '@ionic/angular';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import {TranslateService} from '@ngx-translate/core';
import {ExerciseService} from 'src/app/exercise.service';
import {CorpusService} from 'src/app/corpus.service';
+import {take} from 'rxjs/operators';
@Component({
selector: 'app-home',
@@ -13,7 +14,6 @@ import {CorpusService} from 'src/app/corpus.service';
styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {
- HelperService = HelperService;
public isCorpusUpdateInProgress = false;
constructor(public navCtrl: NavController,
@@ -22,49 +22,51 @@ export class HomePage implements OnInit {
public translate: TranslateService,
public corpusService: CorpusService,
public toastCtrl: ToastController,
+ public helperService: HelperService,
) {
}
- changeLanguage(newLanguage: string) {
- if (this.translate.currentLang !== newLanguage) {
- this.translate.use(newLanguage).subscribe(() => {
- HelperService.loadTranslations(this.translate);
- this.corpusService.initPhenomenonMap();
- this.corpusService.processAnnisResponse(this.corpusService.annisResponse);
- this.corpusService.adjustTranslations();
- });
- }
+ changeLanguage(newLanguage: string): Promise {
+ return new Promise(resolve => {
+ if (this.translate.currentLang !== newLanguage) {
+ this.translate.use(newLanguage).pipe(take(1)).subscribe(() => {
+ this.helperService.loadTranslations(this.translate);
+ this.corpusService.initPhenomenonMap();
+ this.corpusService.processAnnisResponse(this.corpusService.annisResponse);
+ this.corpusService.adjustTranslations().then();
+ return resolve();
+ });
+ } else {
+ return resolve();
+ }
+ });
}
- ionViewDidEnter() {
- HelperService.loadTranslations(this.translate);
+ ionViewDidEnter(): void {
+ this.helperService.loadTranslations(this.translate);
}
- ngOnInit() {
+ ngOnInit(): void {
// fix footer layout on IE11
- if (HelperService.isIE11) {
- const tabs: HTMLElement = document.querySelector('#tabs') as HTMLElement;
+ if (this.helperService.isIE11) {
+ const tabs: HTMLIonTabsElement = document.querySelector('#tabs') as HTMLIonTabsElement;
if (tabs) {
tabs.style.maxWidth = '65%';
}
}
}
- refreshCorpora() {
- this.isCorpusUpdateInProgress = true;
- this.corpusService.getCorpora(0).then(async () => {
- this.isCorpusUpdateInProgress = false;
- const toast = await this.toastCtrl.create({
- message: HelperService.corpusUpdateCompletedString,
- duration: 3000,
- position: 'top'
+ refreshCorpora(): Promise {
+ return new Promise((resolve, reject) => {
+ this.isCorpusUpdateInProgress = true;
+ this.corpusService.getCorpora(0).then(() => {
+ this.isCorpusUpdateInProgress = false;
+ this.helperService.showToast(this.toastCtrl, this.helperService.corpusUpdateCompletedString).then();
+ return resolve();
+ }, async (error: HttpErrorResponse) => {
+ this.isCorpusUpdateInProgress = false;
+ return reject();
});
- toast.present().then();
- }, async (error: HttpErrorResponse) => {
- this.isCorpusUpdateInProgress = false;
});
}
-
- test() {
- }
}
diff --git a/src/app/imprint/imprint.page.html b/src/app/imprint/imprint.page.html
index 7bd4ef0..f036ec5 100644
--- a/src/app/imprint/imprint.page.html
+++ b/src/app/imprint/imprint.page.html
@@ -2,12 +2,12 @@
-
+
{{ 'IMPRINT' | translate }}
@@ -114,19 +114,19 @@
-
+
{{ 'ABOUT' | translate }}
-
+
{{ 'DOC_SOFTWARE' | translate}}
-
+
{{'DOC_EXERCISES' | translate}}
-
+
{{'DOC_VOC_UNIT' | translate}}
diff --git a/src/app/imprint/imprint.page.ts b/src/app/imprint/imprint.page.ts
index 262fb03..25a7257 100644
--- a/src/app/imprint/imprint.page.ts
+++ b/src/app/imprint/imprint.page.ts
@@ -5,24 +5,16 @@ import {HttpClient} from '@angular/common/http';
import {TranslateService} from '@ngx-translate/core';
@Component({
- selector: 'app-imprint',
- templateUrl: './imprint.page.html',
- styleUrls: ['./imprint.page.scss'],
+ selector: 'app-imprint',
+ templateUrl: './imprint.page.html',
+ styleUrls: ['./imprint.page.scss'],
})
export class ImprintPage {
- HelperService = HelperService;
-
- constructor(public navCtrl: NavController,
- public http: HttpClient,
- public translate: TranslateService) { }
-
- goToAuthorPage() {
- this.navCtrl.navigateForward('/author').then();
- }
-
- goToTestPage() {
- this.navCtrl.navigateForward('/test').then();
- }
+ constructor(public navCtrl: NavController,
+ public http: HttpClient,
+ public translate: TranslateService,
+ public helperService: HelperService) {
+ }
}
diff --git a/src/app/info/info.page.html b/src/app/info/info.page.html
index 6badd47..fb4bfae 100644
--- a/src/app/info/info.page.html
+++ b/src/app/info/info.page.html
@@ -2,12 +2,12 @@
-
+
{{ 'ABOUT' | translate }}
@@ -77,19 +77,19 @@
-
+
{{ 'DOC_SOFTWARE' | translate}}
-
+
{{'DOC_EXERCISES' | translate}}
-
+
{{'DOC_VOC_UNIT' | translate}}
-
+
{{ 'IMPRINT' | translate }}
diff --git a/src/app/info/info.page.ts b/src/app/info/info.page.ts
index 252316c..b65ef50 100644
--- a/src/app/info/info.page.ts
+++ b/src/app/info/info.page.ts
@@ -1,4 +1,4 @@
-import {Component, OnInit} from '@angular/core';
+import {Component} from '@angular/core';
import {HelperService} from 'src/app/helper.service';
import {NavController} from '@ionic/angular';
import {HttpClient} from '@angular/common/http';
@@ -11,11 +11,11 @@ import {TranslateService} from '@ngx-translate/core';
})
export class InfoPage {
- HelperService = HelperService;
studiesIndices: number[] = [...Array(4).keys()];
constructor(public navCtrl: NavController,
public http: HttpClient,
- public translate: TranslateService) {
+ public translate: TranslateService,
+ public helperService: HelperService) {
}
}
diff --git a/src/app/kwic/kwic.page.html b/src/app/kwic/kwic.page.html
index 6f5bf33..edf32b0 100644
--- a/src/app/kwic/kwic.page.html
+++ b/src/app/kwic/kwic.page.html
@@ -2,12 +2,12 @@
-
+
{{ 'KWIC' | translate }}
@@ -21,11 +21,10 @@
-
-
+
diff --git a/src/app/kwic/kwic.page.ts b/src/app/kwic/kwic.page.ts
index 8302914..08994df 100644
--- a/src/app/kwic/kwic.page.ts
+++ b/src/app/kwic/kwic.page.ts
@@ -1,4 +1,4 @@
-import {Component} from '@angular/core';
+import {Component, OnInit} from '@angular/core';
import {NavController} from '@ionic/angular';
import {ExerciseService} from 'src/app/exercise.service';
import {HelperService} from '../helper.service';
@@ -8,16 +8,20 @@ import {HelperService} from '../helper.service';
templateUrl: './kwic.page.html',
styleUrls: ['./kwic.page.scss'],
})
-export class KwicPage {
+export class KwicPage implements OnInit {
- HelperService = HelperService;
+ public svgElementSelector = '#svg';
constructor(public navCtrl: NavController,
- public exerciseService: ExerciseService) {
- setTimeout(this.initVisualization.bind(this), 250);
+ public exerciseService: ExerciseService,
+ public helperService: HelperService) {
}
public initVisualization() {
- document.getElementById('svg').innerHTML = this.exerciseService.kwicGraphs;
+ document.querySelector(this.svgElementSelector).innerHTML = this.exerciseService.kwicGraphs;
+ }
+
+ ngOnInit(): void {
+ setTimeout(this.initVisualization.bind(this), 250);
}
}
diff --git a/src/app/models/h5pEventDispatcherMock.ts b/src/app/models/h5pEventDispatcherMock.ts
new file mode 100644
index 0000000..ed6ed50
--- /dev/null
+++ b/src/app/models/h5pEventDispatcherMock.ts
@@ -0,0 +1,30 @@
+import {XAPIevent} from './xAPIevent';
+import Result from './xAPI/Result';
+import StatementBase from './xAPI/StatementBase';
+import Verb from './xAPI/Verb';
+
+export default class H5PeventDispatcherMock {
+ listeners: { [eventName: string]: any[] } = {};
+
+ public on(eventName: string, callback: any) {
+ if (!this.listeners[eventName]) {
+ this.listeners[eventName] = [];
+ }
+ this.listeners[eventName].push(callback);
+ }
+
+ public trigger(eventName: string, event: any) {
+ this.listeners[eventName].forEach(value => value(event));
+ }
+
+ public triggerXAPI(verb: string, result: Result) {
+ this.trigger('xAPI', new XAPIevent({
+ data: {
+ statement: new StatementBase({
+ result,
+ verb: new Verb({id: verb})
+ })
+ }
+ }));
+ };
+}
diff --git a/src/app/models/mock.ts b/src/app/models/mock.ts
deleted file mode 100644
index 630a9f9..0000000
--- a/src/app/models/mock.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import {CorpusMC} from './corpusMC';
-import {ExerciseMC} from './exerciseMC';
-import {MoodleExerciseType} from './enum';
-
-export default class MockMC {
- static apiResponseCorporaGet: object = {
- corpora: [new CorpusMC({
- author: 'author',
- source_urn: 'urn',
- title: 'title',
- })]
- };
- static apiResponseExerciseListGet: ExerciseMC[] = [new ExerciseMC({
- eid: 'eid',
- exercise_type: MoodleExerciseType.cloze.toString(),
- exercise_type_translation: 'exercise_type_translation',
- work_author: 'work_author',
- work_title: 'work_title',
- })];
-}
diff --git a/src/app/models/mockMC.ts b/src/app/models/mockMC.ts
new file mode 100644
index 0000000..b659621
--- /dev/null
+++ b/src/app/models/mockMC.ts
@@ -0,0 +1,61 @@
+import {CorpusMC} from './corpusMC';
+import {ExerciseMC} from './exerciseMC';
+import {MoodleExerciseType, PartOfSpeechValue, Phenomenon} from './enum';
+import {FrequencyItem} from './frequencyItem';
+import {ApplicationState} from './applicationState';
+import {TextData} from './textData';
+import {AnnisResponse} from './annisResponse';
+import {NodeMC} from './nodeMC';
+import {TestResultMC} from './testResultMC';
+import StatementBase from './xAPI/StatementBase';
+import Result from './xAPI/Result';
+import Score from './xAPI/Score';
+
+export default class MockMC {
+ static apiResponseCorporaGet: object = {
+ corpora: [new CorpusMC({
+ author: 'author',
+ source_urn: 'urn',
+ title: 'title',
+ })]
+ };
+ static apiResponseExerciseListGet: ExerciseMC[] = [new ExerciseMC({
+ eid: 'eid',
+ exercise_type: MoodleExerciseType.cloze.toString(),
+ exercise_type_translation: 'exercise_type_translation',
+ work_author: 'work_author',
+ work_title: 'work_title',
+ })];
+ static apiResponseFrequencyAnalysisGet: FrequencyItem[] = [new FrequencyItem({
+ phenomena: [Phenomenon.partOfSpeech.toString()],
+ values: [PartOfSpeechValue.adjective.toString()]
+ })];
+ static apiResponseTextGet: AnnisResponse = new AnnisResponse({
+ nodes: [new NodeMC({udep_lemma: 'lemma', annis_tok: 'tok'})],
+ links: []
+ });
+ static applicationState: ApplicationState = new ApplicationState({
+ currentSetup: new TextData({currentCorpus: new CorpusMC()}),
+ mostRecentSetup: new TextData({annisResponse: new AnnisResponse({nodes: [new NodeMC()], links: []})}),
+ exerciseList: [new ExerciseMC()]
+ });
+ static popoverController: any = {create: () => Promise.resolve({present: () => Promise.resolve()})};
+ static testResults: { [exerciseIndex: number]: TestResultMC } = {
+ 20: new TestResultMC({
+ statement: new StatementBase({result: new Result({score: new Score({scaled: 0, raw: 0})})})
+ })
+ };
+ static toastController: any = {create: () => Promise.resolve({present: () => Promise.resolve()})};
+
+ static addIframe(h5pIframeString: string, buttonClass: string = null): HTMLIFrameElement {
+ const iframe: HTMLIFrameElement = document.createElement('iframe');
+ iframe.setAttribute('id', h5pIframeString.slice(1));
+ document.body.appendChild(iframe);
+ if (buttonClass) {
+ const button: HTMLButtonElement = iframe.contentWindow.document.createElement('button');
+ button.classList.add(buttonClass.slice(1));
+ iframe.contentWindow.document.body.appendChild(button);
+ }
+ return document.querySelector(h5pIframeString);
+ }
+}
diff --git a/src/app/models/xAPI/Activity.ts b/src/app/models/xAPI/Activity.ts
index afe4be4..27800ef 100644
--- a/src/app/models/xAPI/Activity.ts
+++ b/src/app/models/xAPI/Activity.ts
@@ -1,9 +1,13 @@
import Definition from './Definition';
-interface Activity {
- objectType: 'Activity';
- id: string;
- definition?: Definition;
+class Activity {
+ objectType: 'Activity';
+ id: string;
+ definition?: Definition;
+
+ constructor(init?: Partial) {
+ Object.assign(this, init);
+ }
}
export default Activity;
diff --git a/src/app/models/xAPI/Context.ts b/src/app/models/xAPI/Context.ts
index 49dfff3..8b64728 100644
--- a/src/app/models/xAPI/Context.ts
+++ b/src/app/models/xAPI/Context.ts
@@ -3,12 +3,16 @@ import Extensions from './Extensions';
import Group from './Group';
import Agent from './Agent';
-interface Context {
- contextActivities?: ContextActivities;
- team?: Group;
- instructor?: Agent;
- registration?: string;
- extensions?: Extensions;
+class Context {
+ contextActivities?: ContextActivities;
+ team?: Group;
+ instructor?: Agent;
+ registration?: string;
+ extensions?: Extensions;
+
+ constructor(init?: Partial) {
+ Object.assign(this, init);
+ }
}
export default Context;
diff --git a/src/app/models/xAPI/ContextActivities.ts b/src/app/models/xAPI/ContextActivities.ts
index a5a26eb..020992d 100644
--- a/src/app/models/xAPI/ContextActivities.ts
+++ b/src/app/models/xAPI/ContextActivities.ts
@@ -1,10 +1,14 @@
import Activity from './Activity';
-interface ContextActivities {
+class ContextActivities {
parent?: Activity[];
grouping?: Activity[];
category?: Activity[];
other?: Activity[];
+
+ constructor(init?: Partial) {
+ Object.assign(this, init);
+ }
}
export default ContextActivities;
diff --git a/src/app/models/xAPI/Definition.ts b/src/app/models/xAPI/Definition.ts
index 67ea877..f59abc2 100644
--- a/src/app/models/xAPI/Definition.ts
+++ b/src/app/models/xAPI/Definition.ts
@@ -1,7 +1,7 @@
import Extensions from './Extensions';
import LanguageMap from './LanguageMap';
-interface Definition {
+class Definition {
readonly name?: LanguageMap;
readonly description?: LanguageMap;
readonly extensions?: Extensions;
@@ -10,6 +10,10 @@ interface Definition {
readonly choices?: { description: LanguageMap, id: string }[];
readonly correctResponsesPattern?: string[];
readonly interactionType?: string;
+
+ constructor(init?: Partial) {
+ Object.assign(this, init);
+ }
}
export default Definition;
diff --git a/src/app/models/xAPI/Result.ts b/src/app/models/xAPI/Result.ts
index 2d4524f..242663b 100644
--- a/src/app/models/xAPI/Result.ts
+++ b/src/app/models/xAPI/Result.ts
@@ -1,11 +1,15 @@
import Extensions from './Extensions';
-import Score from "src/app/models/xAPI/Score";
+import Score from 'src/app/models/xAPI/Score';
-interface Result {
- duration?: string;
- extensions?: Extensions;
- response?: string;
- score?: Score;
+class Result {
+ duration?: string;
+ extensions?: Extensions;
+ response?: string;
+ score?: Score;
+
+ constructor(init?: Partial) {
+ Object.assign(this, init);
+ }
}
export default Result;
diff --git a/src/app/models/xAPI/Score.ts b/src/app/models/xAPI/Score.ts
index 8b0c45c..820363b 100644
--- a/src/app/models/xAPI/Score.ts
+++ b/src/app/models/xAPI/Score.ts
@@ -1,8 +1,12 @@
-interface Score {
+class Score {
max: number;
min: number;
raw: number;
scaled: number;
+
+ constructor(init?: Partial) {
+ Object.assign(this, init);
+ }
}
export default Score;
diff --git a/src/app/models/xAPI/StatementBase.ts b/src/app/models/xAPI/StatementBase.ts
index 5fb8a2e..94f2dd1 100644
--- a/src/app/models/xAPI/StatementBase.ts
+++ b/src/app/models/xAPI/StatementBase.ts
@@ -5,13 +5,17 @@ import Context from './Context';
import Result from './Result';
import StatementObject from './StatementObject';
-interface StatementBase {
- actor: Actor;
- object: StatementObject;
- verb: Verb;
- context?: Context;
- result?: Result;
- attachments?: Attachment[];
+class StatementBase {
+ actor: Actor;
+ object: StatementObject;
+ verb: Verb;
+ context?: Context;
+ result?: Result;
+ attachments?: Attachment[];
+
+ constructor(init?: Partial) {
+ Object.assign(this, init);
+ }
}
export default StatementBase;
diff --git a/src/app/models/xAPI/Verb.ts b/src/app/models/xAPI/Verb.ts
index bdfa157..28225fe 100644
--- a/src/app/models/xAPI/Verb.ts
+++ b/src/app/models/xAPI/Verb.ts
@@ -1,8 +1,12 @@
import LanguageMap from './LanguageMap';
-interface Verb {
- id: string;
- display?: LanguageMap;
+class Verb {
+ id: string;
+ display?: LanguageMap;
+
+ constructor(init?: Partial) {
+ Object.assign(this, init);
+ }
}
export default Verb;
diff --git a/src/app/preview/preview.page.html b/src/app/preview/preview.page.html
index 46a7d42..57bc606 100644
--- a/src/app/preview/preview.page.html
+++ b/src/app/preview/preview.page.html
@@ -2,12 +2,12 @@
-
+
{{ 'PREVIEW' | translate }}
@@ -24,7 +24,7 @@
-
+
@@ -66,12 +66,12 @@ beginning that it is going to be a download (instead of an ordinary link or clic
-
+
{{ "CHANGE_TEXT_RANGE" | translate}}
-
+
{{ "SHARE" | translate}}
@@ -80,7 +80,7 @@ beginning that it is going to be a download (instead of an ordinary link or clic
+ value="{{helperService.baseUrl + '/' + configMC.frontendExercisePage + '?eid=' + this.corpusService.annisResponse.exercise_id}}"/>
diff --git a/src/app/preview/preview.page.spec.ts b/src/app/preview/preview.page.spec.ts
index 62f6aa5..97c2707 100644
--- a/src/app/preview/preview.page.spec.ts
+++ b/src/app/preview/preview.page.spec.ts
@@ -8,10 +8,27 @@ import {RouterModule} from '@angular/router';
import {TranslateTestingModule} from '../translate-testing/translate-testing.module';
import {FormsModule} from '@angular/forms';
import {APP_BASE_HREF} from '@angular/common';
+import {CorpusService} from '../corpus.service';
+import {ToastController} from '@ionic/angular';
+import MockMC from '../models/mockMC';
+import {AnnisResponse} from '../models/annisResponse';
+import {Solution} from '../models/solution';
+import {ExerciseType} from '../models/enum';
+import {SolutionElement} from '../models/solutionElement';
+import Spy = jasmine.Spy;
+import {NodeMC} from '../models/nodeMC';
+import {TestResultMC} from '../models/testResultMC';
+import H5PeventDispatcherMock from '../models/h5pEventDispatcherMock';
+import Result from '../models/xAPI/Result';
+import configMC from '../../configMC';
+
+declare var H5P: any;
describe('PreviewPage', () => {
- let component: PreviewPage;
+ let previewPage: PreviewPage;
let fixture: ComponentFixture
;
+ let corpusService: CorpusService;
+ let checkAnnisResponseSpy: Spy;
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -25,19 +42,136 @@ describe('PreviewPage', () => {
],
providers: [
{provide: APP_BASE_HREF, useValue: '/'},
+ {provide: ToastController, useValue: MockMC.toastController}
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
- .compileComponents();
+ .compileComponents().then();
+ corpusService = TestBed.inject(CorpusService);
+ fixture = TestBed.createComponent(PreviewPage);
+ previewPage = fixture.componentInstance;
+ checkAnnisResponseSpy = spyOn(corpusService, 'checkAnnisResponse').and.callFake(() => Promise.reject());
+ fixture.detectChanges();
}));
beforeEach(() => {
- fixture = TestBed.createComponent(PreviewPage);
- component = fixture.componentInstance;
- fixture.detectChanges();
});
it('should create', () => {
- expect(component).toBeTruthy();
+ expect(previewPage).toBeTruthy();
+ });
+
+ it('should copy the link', () => {
+ previewPage.helperService.isVocabularyCheck = true;
+ previewPage.corpusService.annisResponse = new AnnisResponse({solutions: []});
+ fixture.detectChanges();
+ const button: HTMLIonButtonElement = document.querySelector('#showShareLinkButton');
+ button.click();
+ fixture.detectChanges();
+ previewPage.copyLink();
+ const input: HTMLInputElement = document.querySelector(previewPage.inputSelector);
+ expect(input.selectionStart).toBe(0);
+ expect(input.selectionEnd).toBe(0);
+ });
+
+ it('should initialize H5P', () => {
+ spyOn(previewPage.exerciseService, 'initH5P').and.returnValue(Promise.resolve());
+ previewPage.corpusService.annisResponse = new AnnisResponse({exercise_id: '', solutions: [new Solution()]});
+ previewPage.currentSolutions = previewPage.corpusService.annisResponse.solutions;
+ previewPage.initH5P();
+ expect(previewPage.solutionIndicesString.length).toBe(0);
+ previewPage.exerciseService.excludeOOV = true;
+ previewPage.corpusService.exercise.type = ExerciseType.markWords;
+ previewPage.initH5P();
+ expect(previewPage.solutionIndicesString.length).toBe(21);
+ });
+
+ it('should be initialized', (done) => {
+ const body: HTMLBodyElement = document.querySelector('body');
+ let iframe: HTMLIFrameElement = document.createElement('iframe');
+ iframe.setAttribute('id', previewPage.exerciseService.h5pIframeString.slice(1));
+ body.appendChild(iframe);
+ spyOn(previewPage, 'sendData').and.returnValue(Promise.resolve());
+ previewPage.ngOnDestroy();
+ const oldDispatcher: any = previewPage.helperService.deepCopy(H5P.externalDispatcher);
+ const newDispatcher: H5PeventDispatcherMock = new H5PeventDispatcherMock();
+ H5P.externalDispatcher = newDispatcher;
+ previewPage.ngOnInit().then(() => {
+ newDispatcher.triggerXAPI(configMC.xAPIverbIDanswered, new Result());
+ checkAnnisResponseSpy.and.returnValue(Promise.resolve());
+ spyOn(previewPage, 'initH5P');
+ spyOn(previewPage, 'processAnnisResponse');
+ previewPage.currentSolutions = [new Solution()];
+ previewPage.ngOnInit().then(() => {
+ expect(previewPage.currentSolutions.length).toBe(0);
+ iframe = document.querySelector(previewPage.exerciseService.h5pIframeString);
+ iframe.parentNode.removeChild(iframe);
+ H5P.externalDispatcher = oldDispatcher;
+ done();
+ });
+ });
+ });
+
+ it('should process an ANNIS response', () => {
+ previewPage.corpusService.annisResponse = new AnnisResponse({});
+ const ar: AnnisResponse = new AnnisResponse({
+ solutions: [new Solution({target: new SolutionElement({content: 'content'})})]
+ });
+ previewPage.processAnnisResponse(ar);
+ expect(previewPage.corpusService.annisResponse.solutions.length).toBe(1);
+ previewPage.corpusService.currentUrn = 'urn:';
+ previewPage.processAnnisResponse(ar);
+ expect(previewPage.corpusService.annisResponse.nodes).toEqual(ar.nodes);
+ });
+
+ it('should process solutions', () => {
+ const solutions: Solution[] = [
+ new Solution({
+ target: new SolutionElement({content: 'content2', salt_id: 'id'}),
+ value: new SolutionElement({salt_id: 'id'})
+ }),
+ new Solution({
+ target: new SolutionElement({content: 'content1', salt_id: 'id'}),
+ value: new SolutionElement({salt_id: 'id'})
+ }),
+ new Solution({
+ target: new SolutionElement({content: 'content1', salt_id: 'id'}),
+ value: new SolutionElement({salt_id: 'id'})
+ }),
+ new Solution({
+ target: new SolutionElement({content: 'content3', salt_id: 'id'}),
+ value: new SolutionElement({salt_id: 'id'})
+ })];
+ previewPage.corpusService.exercise.type = ExerciseType.markWords;
+ previewPage.exerciseService.excludeOOV = true;
+ previewPage.corpusService.annisResponse = new AnnisResponse({
+ nodes: [new NodeMC({is_oov: false, id: 'id'})],
+ solutions
+ });
+ previewPage.processSolutions(solutions);
+ expect(previewPage.currentSolutions[2]).toBe(solutions[0]);
+ });
+
+ it('should send data', (done) => {
+ const requestSpy: Spy = spyOn(previewPage.helperService, 'makePostRequest').and.returnValue(Promise.resolve());
+ const consoleSpy: Spy = spyOn(console, 'log');
+ previewPage.sendData(new TestResultMC()).then(() => {
+ expect(consoleSpy).toHaveBeenCalledTimes(0);
+ requestSpy.and.callFake(() => Promise.reject());
+ previewPage.sendData(new TestResultMC()).then(() => {
+ }, () => {
+ expect(consoleSpy).toHaveBeenCalledTimes(1);
+ done();
+ });
+ });
+ });
+
+ it('should switch OOV', () => {
+ previewPage.currentSolutions = [new Solution()];
+ previewPage.corpusService.annisResponse = new AnnisResponse();
+ spyOn(previewPage, 'processSolutions');
+ spyOn(previewPage, 'initH5P');
+ previewPage.switchOOV();
+ expect(previewPage.currentSolutions.length).toBe(0);
});
});
diff --git a/src/app/preview/preview.page.ts b/src/app/preview/preview.page.ts
index d0beb9a..940bc43 100644
--- a/src/app/preview/preview.page.ts
+++ b/src/app/preview/preview.page.ts
@@ -8,7 +8,7 @@ import {CorpusService} from 'src/app/corpus.service';
import {Component, OnDestroy, OnInit} from '@angular/core';
import {TranslateService} from '@ngx-translate/core';
import {Solution} from 'src/app/models/solution';
-import {HttpClient, HttpErrorResponse} from '@angular/common/http';
+import {HttpClient} from '@angular/common/http';
import {XAPIevent} from 'src/app/models/xAPIevent';
import {TestResultMC} from 'src/app/models/testResultMC';
import configMC from '../../configMC';
@@ -26,7 +26,6 @@ export class PreviewPage implements OnDestroy, OnInit {
public ExerciseType = ExerciseType;
public FileType = FileType;
public currentSolutions: Solution[];
- public HelperService = HelperService;
public inputSelector = 'input[type="text"]';
public maxGapLength = 0;
public showShareLink = false;
@@ -43,15 +42,6 @@ export class PreviewPage implements OnDestroy, OnInit {
public helperService: HelperService,
public toastCtrl: ToastController,
public storage: Storage) {
- this.currentSolutions = [];
- if (!HelperService.isVocabularyCheck) {
- this.exerciseService.excludeOOV = false;
- }
- this.corpusService.checkAnnisResponse().then(() => {
- this.processAnnisResponse(this.corpusService.annisResponse);
- this.initH5P();
- }, () => {
- });
}
async copyLink(): Promise {
@@ -59,12 +49,7 @@ export class PreviewPage implements OnDestroy, OnInit {
input.select();
document.execCommand('copy');
input.setSelectionRange(0, 0);
- const toast = await this.toastCtrl.create({
- message: this.corpusService.shareLinkCopiedString,
- duration: 3000,
- position: 'middle'
- });
- toast.present().then();
+ this.helperService.showToast(this.toastCtrl, this.corpusService.shareLinkCopiedString, 'middle').then();
}
initH5P(): void {
@@ -76,7 +61,7 @@ export class PreviewPage implements OnDestroy, OnInit {
// this has to be LocalStorage because the H5P javascript cannot easily access the Ionic Storage
window.localStorage.setItem(configMC.localStorageKeyH5P, url);
const exerciseTypePath: string = this.corpusService.exercise.type === ExerciseType.markWords ? 'mark_words' : 'drag_text';
- this.exerciseService.initH5P(exerciseTypePath);
+ this.exerciseService.initH5P(exerciseTypePath).then();
this.updateFileUrl();
}
@@ -84,21 +69,34 @@ export class PreviewPage implements OnDestroy, OnInit {
H5P.externalDispatcher.off('xAPI');
}
- ngOnInit(): void {
- H5P.externalDispatcher.on('xAPI', (event: XAPIevent) => {
- // results are only available when a task has been completed/answered, not in the "attempted" or "interacted" stages
- if (event.data.statement.verb.id === configMC.xAPIverbIDanswered && event.data.statement.result) {
- const iframe: HTMLIFrameElement = document.querySelector(this.exerciseService.h5pIframeString);
- if (iframe) {
- const iframeDoc: Document = iframe.contentWindow.document;
- const inner: string = iframeDoc.documentElement.innerHTML;
- const result: TestResultMC = new TestResultMC({
- statement: event.data.statement,
- innerHTML: inner
- });
- this.sendData(result);
- }
+ ngOnInit(): Promise {
+ return new Promise((resolve) => {
+ this.currentSolutions = [];
+ if (!this.helperService.isVocabularyCheck) {
+ this.exerciseService.excludeOOV = false;
}
+ H5P.externalDispatcher.on('xAPI', (event: XAPIevent) => {
+ // results are only available when a task has been completed/answered, not in the "attempted" or "interacted" stages
+ if (event.data.statement.verb.id === configMC.xAPIverbIDanswered && event.data.statement.result) {
+ const iframe: HTMLIFrameElement = document.querySelector(this.exerciseService.h5pIframeString);
+ if (iframe) {
+ const iframeDoc: Document = iframe.contentWindow.document;
+ const inner: string = iframeDoc.documentElement.innerHTML;
+ const result: TestResultMC = new TestResultMC({
+ statement: event.data.statement,
+ innerHTML: inner
+ });
+ this.sendData(result).then();
+ }
+ }
+ });
+ this.corpusService.checkAnnisResponse().then(() => {
+ this.processAnnisResponse(this.corpusService.annisResponse);
+ this.initH5P();
+ return resolve();
+ }, () => {
+ return resolve();
+ });
});
}
@@ -137,18 +135,17 @@ export class PreviewPage implements OnDestroy, OnInit {
ta.select();
}
- sendData(result: TestResultMC): void {
- const fileUrl: string = configMC.backendBaseUrl + configMC.backendApiFilePath;
- HelperService.currentError = null;
- HelperService.isLoading = true;
- const formData = new FormData();
- formData.append('learning_result', JSON.stringify(result.statement));
- this.http.post(fileUrl, formData).subscribe(async () => {
- HelperService.isLoading = false;
- }, async (error: HttpErrorResponse) => {
- HelperService.isLoading = false;
- HelperService.currentError = error;
- console.log('ERROR: COULD NOT SEND EXERCISE RESULTS TO SERVER.');
+ sendData(result: TestResultMC): Promise {
+ return new Promise((resolve, reject) => {
+ const fileUrl: string = configMC.backendBaseUrl + configMC.backendApiFilePath;
+ const formData = new FormData();
+ formData.append('learning_result', JSON.stringify(result.statement));
+ this.helperService.makePostRequest(this.http, this.toastCtrl, fileUrl, formData, '').then(() => {
+ return resolve();
+ }, () => {
+ console.log('ERROR: COULD NOT SEND EXERCISE RESULTS TO SERVER.');
+ return reject();
+ });
});
}
diff --git a/src/app/ranking/ranking.page.html b/src/app/ranking/ranking.page.html
index f84e8cd..e6b6de7 100644
--- a/src/app/ranking/ranking.page.html
+++ b/src/app/ranking/ranking.page.html
@@ -2,12 +2,12 @@
-
+
{{ 'VOCABULARY_RANKING' | translate }}
diff --git a/src/app/ranking/ranking.page.spec.ts b/src/app/ranking/ranking.page.spec.ts
index dac8fbe..d13b82b 100644
--- a/src/app/ranking/ranking.page.spec.ts
+++ b/src/app/ranking/ranking.page.spec.ts
@@ -7,10 +7,16 @@ import {IonicStorageModule} from '@ionic/storage';
import {RouterModule} from '@angular/router';
import {TranslateTestingModule} from '../translate-testing/translate-testing.module';
import {APP_BASE_HREF} from '@angular/common';
+import {CorpusService} from '../corpus.service';
+import {Sentence} from '../models/sentence';
+import Spy = jasmine.Spy;
+import {AnnisResponse} from '../models/annisResponse';
+import {NodeMC} from '../models/nodeMC';
describe('RankingPage', () => {
- let component: RankingPage;
+ let rankingPage: RankingPage;
let fixture: ComponentFixture;
+ let corpusService: CorpusService;
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -26,16 +32,35 @@ describe('RankingPage', () => {
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
- .compileComponents();
+ .compileComponents().then();
+ corpusService = TestBed.inject(CorpusService);
}));
beforeEach(() => {
fixture = TestBed.createComponent(RankingPage);
- component = fixture.componentInstance;
+ rankingPage = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
- expect(component).toBeTruthy();
+ expect(rankingPage).toBeTruthy();
+ });
+
+ it('should show the text', (done) => {
+ rankingPage.helperService.isVocabularyCheck = false;
+ const vocCheckSpy: Spy = spyOn(rankingPage.vocService, 'getVocabularyCheck').and.returnValue(Promise.resolve(
+ new AnnisResponse({nodes: [new NodeMC({id: 'id/id:1-2/id'})]})));
+ spyOn(rankingPage.corpusService, 'processAnnisResponse');
+ spyOn(rankingPage.helperService, 'goToShowTextPage').and.returnValue(Promise.resolve(true));
+ rankingPage.showText([new Sentence({id: 1})]).then(() => {
+ expect(rankingPage.helperService.isVocabularyCheck).toBe(true);
+ vocCheckSpy.and.callFake(() => Promise.reject());
+ rankingPage.helperService.isVocabularyCheck = false;
+ rankingPage.showText([new Sentence({id: 1})]).then(() => {
+ }, () => {
+ expect(rankingPage.helperService.isVocabularyCheck).toBe(false);
+ done();
+ });
+ });
});
});
diff --git a/src/app/ranking/ranking.page.ts b/src/app/ranking/ranking.page.ts
index fcd0652..331a3e5 100644
--- a/src/app/ranking/ranking.page.ts
+++ b/src/app/ranking/ranking.page.ts
@@ -14,28 +14,32 @@ import {Sentence} from 'src/app/models/sentence';
styleUrls: ['./ranking.page.scss'],
})
export class RankingPage {
- HelperService = HelperService;
Math = Math;
constructor(public navCtrl: NavController,
public corpusService: CorpusService,
public vocService: VocabularyService,
public exerciseService: ExerciseService,
- public toastCtrl: ToastController) {
+ public toastCtrl: ToastController,
+ public helperService: HelperService) {
// remove old sentence boundaries
this.corpusService.baseUrn = this.corpusService.currentUrn.split('@')[0];
}
- showText(rank: Sentence[]) {
- this.corpusService.currentUrn = this.corpusService.baseUrn + `@${rank[0].id}-${rank[rank.length - 1].id}`;
- this.vocService.getVocabularyCheck(this.corpusService.currentUrn, true).then((ar: AnnisResponse) => {
- const urnStart: string = ar.nodes[0].id.split('/')[1];
- const urnEnd: string = ar.nodes.slice(-1)[0].id.split('/')[1];
- this.corpusService.currentUrn = urnStart.concat('-', urnEnd.split(':').slice(-1)[0]);
- this.corpusService.processAnnisResponse(ar);
- HelperService.isVocabularyCheck = true;
- HelperService.goToShowTextPage(this.navCtrl, true).then();
- }, async (error: HttpErrorResponse) => {
+ showText(rank: Sentence[]): Promise {
+ return new Promise((resolve, reject) => {
+ this.corpusService.currentUrn = this.corpusService.baseUrn + `@${rank[0].id}-${rank[rank.length - 1].id}`;
+ this.vocService.getVocabularyCheck(this.corpusService.currentUrn, true).then((ar: AnnisResponse) => {
+ const urnStart: string = ar.nodes[0].id.split('/')[1];
+ const urnEnd: string = ar.nodes.slice(-1)[0].id.split('/')[1];
+ this.corpusService.currentUrn = urnStart.concat('-', urnEnd.split(':').slice(-1)[0]);
+ this.corpusService.processAnnisResponse(ar);
+ this.helperService.isVocabularyCheck = true;
+ this.helperService.goToShowTextPage(this.navCtrl, true).then();
+ return resolve();
+ }, async (error: HttpErrorResponse) => {
+ return reject();
+ });
});
}
diff --git a/src/app/semantics/semantics-routing.module.ts b/src/app/semantics/semantics-routing.module.ts
new file mode 100644
index 0000000..d575b00
--- /dev/null
+++ b/src/app/semantics/semantics-routing.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { SemanticsPage } from './semantics.page';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: SemanticsPage
+ }
+];
+
+@NgModule({
+ imports: [RouterModule.forChild(routes)],
+ exports: [RouterModule],
+})
+export class SemanticsPageRoutingModule {}
diff --git a/src/app/semantics/semantics.module.ts b/src/app/semantics/semantics.module.ts
new file mode 100644
index 0000000..2a22a27
--- /dev/null
+++ b/src/app/semantics/semantics.module.ts
@@ -0,0 +1,23 @@
+import {NgModule} from '@angular/core';
+import {CommonModule} from '@angular/common';
+import {FormsModule} from '@angular/forms';
+
+import {IonicModule} from '@ionic/angular';
+
+import {SemanticsPageRoutingModule} from './semantics-routing.module';
+
+import {SemanticsPage} from './semantics.page';
+import {TranslateModule} from '@ngx-translate/core';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ SemanticsPageRoutingModule,
+ TranslateModule.forChild(),
+ ],
+ declarations: [SemanticsPage]
+})
+export class SemanticsPageModule {
+}
diff --git a/src/app/semantics/semantics.page.html b/src/app/semantics/semantics.page.html
new file mode 100644
index 0000000..c5bdebf
--- /dev/null
+++ b/src/app/semantics/semantics.page.html
@@ -0,0 +1,74 @@
+
+
+
+
+
+ {{ 'SEMANTICS' | translate }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ 'SEARCH' | translate }}
+
+
+
+
+
+
+ {{ 'MINIMUM_WORD_FREQUENCY_COUNT' | translate }}
+
+
+
+
+
+
+ {{ 'NEAREST_NEIGHBORS_COUNT' | translate }}
+
+
+
+
+
+
+ {{ 'HIGHLIGHT' | translate }}
+
+
+
+
+
+
+ {{ 'APPLY' | translate }}
+
+
+
+
+
+
+
+
+
+
+ {{part}}
+
+
+
+
+ {{ 'BACK' | translate }}
+
+
+
+
diff --git a/src/app/semantics/semantics.page.scss b/src/app/semantics/semantics.page.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/semantics/semantics.page.spec.ts b/src/app/semantics/semantics.page.spec.ts
new file mode 100644
index 0000000..4f5cb4a
--- /dev/null
+++ b/src/app/semantics/semantics.page.spec.ts
@@ -0,0 +1,74 @@
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {IonicModule} from '@ionic/angular';
+
+import {SemanticsPage} from './semantics.page';
+import {RouterModule} from '@angular/router';
+import {HttpClientModule, HttpErrorResponse} from '@angular/common/http';
+import {IonicStorageModule} from '@ionic/storage';
+import {TranslateTestingModule} from '../translate-testing/translate-testing.module';
+import {FormsModule} from '@angular/forms';
+import {of} from 'rxjs';
+import {APP_BASE_HREF} from '@angular/common';
+import Spy = jasmine.Spy;
+
+describe('SemanticsPage', () => {
+ let semanticsPage: SemanticsPage;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [SemanticsPage],
+ imports: [
+ FormsModule,
+ HttpClientModule,
+ IonicStorageModule.forRoot(),
+ RouterModule.forRoot([]),
+ TranslateTestingModule,
+ ],
+ providers: [
+ {provide: APP_BASE_HREF, useValue: '/'},
+ ],
+ }).compileComponents().then();
+ fixture = TestBed.createComponent(SemanticsPage);
+ semanticsPage = fixture.componentInstance;
+ fixture.detectChanges();
+ }));
+
+ it('should create', () => {
+ expect(semanticsPage).toBeTruthy();
+ });
+
+ it('should be initialized', (done) => {
+ spyOn(semanticsPage, 'updateVectorNetwork');
+ semanticsPage.activatedRoute.queryParams = of({minCount: '0'});
+ semanticsPage.ngOnInit().then(() => {
+ expect(semanticsPage.minCount).toBe(0);
+ semanticsPage.activatedRoute.queryParams = of({
+ searchRegex: 'a',
+ highlightRegex: 'b',
+ nearestNeighborCount: '0'
+ });
+ semanticsPage.ngOnInit().then(() => {
+ expect(semanticsPage.nearestNeighborCount).toBe(0);
+ done();
+ });
+ });
+ });
+
+ it('should update the vector network', (done) => {
+ const requestSpy: Spy = spyOn(semanticsPage.helperService, 'makeGetRequest').and.returnValue(Promise.resolve('a'));
+ const toastSpy: Spy = spyOn(semanticsPage.helperService, 'showToast').and.returnValue(Promise.resolve());
+ semanticsPage.updateVectorNetwork().then(() => {
+ expect(toastSpy).toHaveBeenCalledTimes(1);
+ semanticsPage.searchRegex = 'a';
+ semanticsPage.updateVectorNetwork().then(() => {
+ expect(semanticsPage.kwicGraphs).toBeTruthy();
+ requestSpy.and.callFake(() => Promise.reject(new HttpErrorResponse({status: 422})));
+ semanticsPage.updateVectorNetwork().then(() => {
+ expect(toastSpy).toHaveBeenCalledTimes(2);
+ done();
+ });
+ });
+ });
+ });
+});
diff --git a/src/app/semantics/semantics.page.ts b/src/app/semantics/semantics.page.ts
new file mode 100644
index 0000000..1463c8d
--- /dev/null
+++ b/src/app/semantics/semantics.page.ts
@@ -0,0 +1,85 @@
+import {Component, OnInit} from '@angular/core';
+import {NavController, ToastController} from '@ionic/angular';
+import {HelperService} from '../helper.service';
+import configMC from '../../configMC';
+import {HttpClient, HttpErrorResponse, HttpParams} from '@angular/common/http';
+import {ActivatedRoute} from '@angular/router';
+import {CorpusService} from '../corpus.service';
+
+@Component({
+ selector: 'app-semantics',
+ templateUrl: './semantics.page.html',
+ styleUrls: ['./semantics.page.scss'],
+})
+export class SemanticsPage implements OnInit {
+ public highlightRegex = '';
+ public kwicGraphs: string;
+ public metadata: string[] = ('XII panegyrici Latini\n' +
+ 'Baehrens, Emil\n' +
+ 'Lipsiae | 1874 | Teubner\n' +
+ 'Augsburg, Staats- und Stadtbibliothek -- LR 759\n' +
+ 'URL: https://reader.digitale-sammlungen.de/de/fs1/object/display/bsb11265534_00133.html\n' +
+ 'Permalink: http://mdz-nbn-resolving.de/urn:nbn:de:bvb:12-bsb11265534-1').split('\n');
+ public minCount = 1;
+ public nearestNeighborCount = 1;
+ public searchRegex = '';
+ public svgElementSelector = '#svg';
+
+ constructor(public navCtrl: NavController,
+ public helperService: HelperService,
+ public http: HttpClient,
+ public toastCtrl: ToastController,
+ public activatedRoute: ActivatedRoute,
+ public corpusService: CorpusService) {
+ }
+
+ ngOnInit(): Promise {
+ return new Promise(resolve => {
+ this.activatedRoute.queryParams.subscribe((params: any) => {
+ if (Object.keys(params).length) {
+ const paramSearchRegex: string = params.searchRegex;
+ this.searchRegex = paramSearchRegex ? paramSearchRegex : this.searchRegex;
+ const paramMinCount: string = params.minCount;
+ this.minCount = paramMinCount ? +paramMinCount : this.minCount;
+ const paramNearestNeighborCount: string = params.nearestNeighborCount;
+ this.nearestNeighborCount = paramNearestNeighborCount ? +paramNearestNeighborCount : this.nearestNeighborCount;
+ const paramHighlightRegex: string = params.highlightRegex;
+ this.highlightRegex = paramHighlightRegex ? paramHighlightRegex : this.highlightRegex;
+ // dirty hack to get the loading spinner displayed correctly
+ setTimeout(this.updateVectorNetwork.bind(this), 500);
+ }
+ return resolve();
+ });
+ });
+ }
+
+ updateVectorNetwork(): Promise {
+ const retVal: Promise = new Promise((resolve, reject) => {
+ if (!this.searchRegex) {
+ this.helperService.showToast(this.toastCtrl, this.corpusService.searchRegexMissingString).then();
+ return reject();
+ }
+ let params: HttpParams = new HttpParams().set('search_regex', this.searchRegex);
+ params = params.set('min_count', (Math.max(Math.round(this.minCount), 1)).toString());
+ params = params.set('highlight_regex', this.highlightRegex);
+ params = params.set('nearest_neighbor_count', (Math.max(Math.round(this.nearestNeighborCount), 1)).toString());
+ const kwicUrl: string = configMC.backendBaseUrl + configMC.backendApiVectorNetworkPath;
+ const svgElement: SVGElement = document.querySelector(this.svgElementSelector);
+ svgElement.innerHTML = '';
+ this.helperService.makeGetRequest(this.http, this.toastCtrl, kwicUrl, params).then((svgString: string) => {
+ this.kwicGraphs = svgString;
+ svgElement.innerHTML = this.kwicGraphs;
+ return resolve();
+ }, (error: HttpErrorResponse) => {
+ if (error.status === 422) {
+ this.helperService.showToast(this.toastCtrl, this.corpusService.tooManyHitsString).then();
+ }
+ return reject();
+ });
+ });
+ // dirty hack to prevent unhandled promise rejection in click events
+ return retVal.catch(() => {
+ });
+ }
+
+}
diff --git a/src/app/show-text/show-text.page.html b/src/app/show-text/show-text.page.html
index c27b910..a439707 100644
--- a/src/app/show-text/show-text.page.html
+++ b/src/app/show-text/show-text.page.html
@@ -2,12 +2,12 @@
-
+
{{cc.title}}
{{corpusService.currentUrn?.split(":")[corpusService.currentUrn?.split(":").length - 1]}}
@@ -26,7 +26,7 @@
{{'SHOW_TEXT_TITLE' | translate}}
0; else loading">
-
+
@@ -109,11 +109,11 @@
{{ 'BACK' | translate }}
-
+
{{ 'VOCABULARY_CHECK' | translate }}
-
+
{{ "EXERCISE_SET_PARAMETERS" | translate}}
diff --git a/src/app/show-text/show-text.page.spec.ts b/src/app/show-text/show-text.page.spec.ts
index a4001b6..590cbcf 100644
--- a/src/app/show-text/show-text.page.spec.ts
+++ b/src/app/show-text/show-text.page.spec.ts
@@ -8,9 +8,13 @@ import {RouterModule} from '@angular/router';
import {TranslateTestingModule} from '../translate-testing/translate-testing.module';
import {FormsModule} from '@angular/forms';
import {APP_BASE_HREF} from '@angular/common';
+import {AnnisResponse} from '../models/annisResponse';
+import {NodeMC} from '../models/nodeMC';
+import {VocabularyCorpus} from '../models/enum';
+import Spy = jasmine.Spy;
describe('ShowTextPage', () => {
- let component: ShowTextPage;
+ let showTextPage: ShowTextPage;
let fixture: ComponentFixture;
beforeEach(async(() => {
@@ -28,16 +32,55 @@ describe('ShowTextPage', () => {
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
- .compileComponents();
+ .compileComponents().then();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ShowTextPage);
- component = fixture.componentInstance;
+ showTextPage = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
- expect(component).toBeTruthy();
+ expect(showTextPage).toBeTruthy();
+ });
+
+ it('should generate a download link', (done) => {
+ showTextPage.corpusService.currentText = 'text';
+ fixture.detectChanges();
+ const requestSpy: Spy = spyOn(showTextPage.helperService, 'makePostRequest').and.returnValue(Promise.resolve('a.b.c'));
+ showTextPage.corpusService.initCurrentCorpus().then(() => {
+ showTextPage.generateDownloadLink('').then(() => {
+ let link: HTMLLinkElement = document.querySelector(showTextPage.downloadLinkSelector);
+ expect(link.href.length).toBe(61);
+ requestSpy.and.callFake(() => Promise.reject());
+ link.style.display = 'none';
+ fixture.detectChanges();
+ showTextPage.generateDownloadLink('').then(() => {
+ }, () => {
+ link = document.querySelector(showTextPage.downloadLinkSelector);
+ expect(link.style.display).toBe('none');
+ done();
+ });
+ });
+ });
+ });
+
+ it('should get whitespace', () => {
+ showTextPage.corpusService.annisResponse = new AnnisResponse({nodes: []});
+ let result: string = showTextPage.getWhiteSpace(0);
+ expect(result.length).toBe(0);
+ showTextPage.corpusService.annisResponse.nodes = [new NodeMC(), new NodeMC()];
+ result = showTextPage.getWhiteSpace(0);
+ expect(result.length).toBe(1);
+ showTextPage.corpusService.annisResponse.nodes[1].annis_tok = '.';
+ result = showTextPage.getWhiteSpace(0);
+ expect(result.length).toBe(0);
+ });
+
+ it('should be initialized', () => {
+ showTextPage.vocService.currentReferenceVocabulary = null;
+ showTextPage.ngOnInit();
+ expect(showTextPage.vocService.currentReferenceVocabulary).toBe(VocabularyCorpus.bws);
});
});
diff --git a/src/app/show-text/show-text.page.ts b/src/app/show-text/show-text.page.ts
index 935bb70..dc87fee 100644
--- a/src/app/show-text/show-text.page.ts
+++ b/src/app/show-text/show-text.page.ts
@@ -19,14 +19,13 @@ import configMC from '../../configMC';
})
export class ShowTextPage implements OnInit {
FileType = FileType;
- HelperService = HelperService;
ObjectKeys = Object.keys;
+ public downloadLinkSelector = '#download';
public highlightOOV = false;
- public text: string;
- public urlBase: string;
public isDownloading = false;
public showTextComplexity = false;
public showTextComplexityDoc = false;
+ public text: string;
public textComplexityMap = {
all: 'TEXT_COMPLEXITY_ALL',
n_w: 'TEXT_COMPLEXITY_WORD_COUNT',
@@ -44,6 +43,7 @@ export class ShowTextPage implements OnInit {
n_gerund: 'TEXT_COMPLEXITY_GERUND_COUNT',
n_abl_abs: 'TEXT_COMPLEXITY_ABLATIVI_ABSOLUTI_COUNT'
};
+ public urlBase: string;
constructor(public navCtrl: NavController,
public corpusService: CorpusService,
@@ -51,45 +51,42 @@ export class ShowTextPage implements OnInit {
public toastCtrl: ToastController,
public translateService: TranslateService,
public vocService: VocabularyService,
- public http: HttpClient) {
- this.urlBase = configMC.backendBaseUrl + configMC.backendApiFilePath;
+ public http: HttpClient,
+ public helperService: HelperService) {
}
- generateDownloadLink(fileType: string) {
- const formData = new FormData();
- let content: string = document.querySelector('.text').outerHTML;
- // add underline elements so we do not need to specify CSS options in the backend's PDF generator
- content = content.replace(/(oov">)(.+?)(<\/span>)/g, '$1$2 $3');
- this.corpusService.currentCorpus.pipe(take(1)).subscribe((cc: CorpusMC) => {
- const authorTitle: string = cc.author + ', ' + cc.title;
- content = `${authorTitle} ${this.corpusService.currentUrn.split(':').slice(-1)[0]}
` + content;
- formData.append('html_content', content);
- formData.append('file_type', fileType);
- formData.append('urn', this.corpusService.currentUrn);
- this.isDownloading = true;
- this.http.post(this.urlBase, formData).subscribe((response: string) => {
- this.isDownloading = false;
- const responseParts: string[] = response.split('.');
- const link: HTMLLinkElement = document.querySelector('#download');
- link.href = configMC.backendBaseUrl + configMC.backendApiFilePath + '?id=' + responseParts[0]
- + '&type=' + responseParts[1];
- link.style.display = 'block';
- }, async (error: any) => {
- this.isDownloading = false;
- HelperService.currentError = error;
- const toast = await this.toastCtrl.create({
- message: HelperService.generalErrorAlertMessage,
- duration: 3000,
- position: 'top'
+ generateDownloadLink(fileType: string): Promise {
+ return new Promise((resolve, reject) => {
+ const formData = new FormData();
+ let content: string = document.querySelector('.text').outerHTML;
+ // add underline elements so we do not need to specify CSS options in the backend's PDF generator
+ content = content.replace(/(oov">)(.+?)(<\/span>)/g, '$1$2 $3');
+ this.corpusService.currentCorpus.pipe(take(1)).subscribe((cc: CorpusMC) => {
+ const authorTitle: string = cc.author + ', ' + cc.title;
+ content = `${authorTitle} ${this.corpusService.currentUrn.split(':').slice(-1)[0]}
` + content;
+ formData.append('html_content', content);
+ formData.append('file_type', fileType);
+ formData.append('urn', this.corpusService.currentUrn);
+ this.isDownloading = true;
+ this.helperService.makePostRequest(this.http, this.toastCtrl, this.urlBase, formData).then((response: string) => {
+ this.isDownloading = false;
+ const responseParts: string[] = response.split('.');
+ const link: HTMLLinkElement = document.querySelector(this.downloadLinkSelector);
+ link.href = configMC.backendBaseUrl + configMC.backendApiFilePath + '?id=' + responseParts[0]
+ + '&type=' + responseParts[1];
+ link.style.display = 'block';
+ return resolve();
+ }, () => {
+ return reject();
});
- toast.present().then();
});
});
}
- getWhiteSpace(index: number) {
+ getWhiteSpace(index: number): string {
if (this.corpusService.annisResponse.nodes[index + 1]) {
- if ('.,\\/#!$%\\^&\\*;:{}=\\-_`~()'.indexOf(this.corpusService.annisResponse.nodes[index + 1].annis_tok) > -1) {
+ if (this.corpusService.annisResponse.nodes[index + 1].annis_tok &&
+ this.corpusService.annisResponse.nodes[index + 1].annis_tok.search(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g) >= 0) {
return '';
}
return ' ';
@@ -98,6 +95,7 @@ export class ShowTextPage implements OnInit {
}
ngOnInit(): void {
+ this.urlBase = configMC.backendBaseUrl + configMC.backendApiFilePath;
this.vocService.currentReferenceVocabulary = this.vocService.currentReferenceVocabulary || VocabularyCorpus.bws;
}
}
diff --git a/src/app/sources/sources.page.html b/src/app/sources/sources.page.html
index 1411c6a..11d0798 100644
--- a/src/app/sources/sources.page.html
+++ b/src/app/sources/sources.page.html
@@ -2,12 +2,12 @@
-
+
{{ 'SOURCES' | translate }}
@@ -136,11 +136,11 @@
-
+
Übung erstellen
-
+
Test beginnen
diff --git a/src/app/sources/sources.page.ts b/src/app/sources/sources.page.ts
index 7c2be12..bdae6e7 100644
--- a/src/app/sources/sources.page.ts
+++ b/src/app/sources/sources.page.ts
@@ -1,28 +1,20 @@
-import { Component } from '@angular/core';
+import {Component} from '@angular/core';
import {HelperService} from 'src/app/helper.service';
import {NavController} from '@ionic/angular';
import {HttpClient} from '@angular/common/http';
import {TranslateService} from '@ngx-translate/core';
@Component({
- selector: 'app-sources',
- templateUrl: './sources.page.html',
- styleUrls: ['./sources.page.scss'],
+ selector: 'app-sources',
+ templateUrl: './sources.page.html',
+ styleUrls: ['./sources.page.scss'],
})
export class SourcesPage {
- HelperService = HelperService;
-
- constructor(public navCtrl: NavController,
- public http: HttpClient,
- public translate: TranslateService) { }
-
- goToAuthorPage() {
- this.navCtrl.navigateForward('/author').then();
- }
-
- goToTestPage() {
- this.navCtrl.navigateForward('/test').then();
- }
+ constructor(public navCtrl: NavController,
+ public http: HttpClient,
+ public translate: TranslateService,
+ public helperService: HelperService) {
+ }
}
diff --git a/src/app/test/test.page.html b/src/app/test/test.page.html
index cd93b2e..6306358 100644
--- a/src/app/test/test.page.html
+++ b/src/app/test/test.page.html
@@ -4,9 +4,9 @@
{{ (currentExerciseIndex == 0 ? 'TEST' : (isTestMode ? 'START_TEST' : 'START_LEARNING')) | translate }}
-
+
-
+
@@ -16,7 +16,7 @@
-
+
Show exercise:
-
+
{{ 'TEST_MODULE_GO_TO_EXERCISE' | translate}}:
- {{ 'START_LEARNING' | translate}}
+ {{ 'START_LEARNING' | translate}}
- {{'START_TEST' | translate }}
+ {{'START_TEST' | translate }}
@@ -102,7 +102,7 @@
-
+
{{ 'BUTTON_CONTINUE' | translate}}
@@ -170,15 +170,15 @@
-
+
{{ 'TEST_MODULE_SEND_DATA' | translate }}
-
+
{{ 'EXERCISE_GENERATE' | translate }}
-
+
{{ 'TEST_REPEAT' | translate }}
diff --git a/src/app/test/test.page.spec.ts b/src/app/test/test.page.spec.ts
index f867601..7a1d5e6 100644
--- a/src/app/test/test.page.spec.ts
+++ b/src/app/test/test.page.spec.ts
@@ -1,21 +1,37 @@
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
-import {async, ComponentFixture, inject, TestBed, TestBedStatic} from '@angular/core/testing';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {TestPage} from './test.page';
import {IonicStorageModule} from '@ionic/storage';
import {RouterModule} from '@angular/router';
import {TranslateTestingModule} from '../translate-testing/translate-testing.module';
-import {PopoverController, ToastController} from '@ionic/angular';
+import {PopoverController} from '@ionic/angular';
import {APP_BASE_HREF} from '@angular/common';
import {HttpClientTestingModule} from '@angular/common/http/testing';
+import {TestResultMC} from '../models/testResultMC';
+import StatementBase from '../models/xAPI/StatementBase';
+import Result from '../models/xAPI/Result';
+import Score from '../models/xAPI/Score';
+import {TestModuleState} from '../models/enum';
+import MockMC from '../models/mockMC';
+import {XAPIevent} from '../models/xAPIevent';
+import Spy = jasmine.Spy;
+import H5PeventDispatcherMock from '../models/h5pEventDispatcherMock';
+import Verb from '../models/xAPI/Verb';
+import configMC from '../../configMC';
+import Context from '../models/xAPI/Context';
+import ContextActivities from '../models/xAPI/ContextActivities';
+import Activity from '../models/xAPI/Activity';
+import Definition from '../models/xAPI/Definition';
+
+declare var H5P: any;
describe('TestPage', () => {
- let component: TestPage;
+ let testPage: TestPage;
let fixture: ComponentFixture;
- let tbs: TestBedStatic;
beforeEach(async(() => {
- tbs = TestBed.configureTestingModule({
+ TestBed.configureTestingModule({
declarations: [TestPage],
imports: [
HttpClientTestingModule,
@@ -25,8 +41,7 @@ describe('TestPage', () => {
],
providers: [
{provide: APP_BASE_HREF, useValue: '/'},
- {provide: ToastController},
- {provide: PopoverController, useValue: {}},
+ {provide: PopoverController, useValue: MockMC.popoverController},
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
});
@@ -34,11 +49,242 @@ describe('TestPage', () => {
beforeEach(() => {
fixture = TestBed.createComponent(TestPage);
- component = fixture.componentInstance;
+ testPage = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
- expect(component).toBeTruthy();
+ expect(testPage).toBeTruthy();
+ });
+
+ it('should adjust the timer', () => {
+ testPage.isTestMode = false;
+ const timerElement: HTMLSpanElement = document.querySelector(testPage.timerIDstring);
+ timerElement.innerHTML = '1';
+ testPage.adjustTimer(1, false);
+ expect(document.querySelector(testPage.timerIDstring).innerHTML).toBe('1');
+ testPage.isTestMode = true;
+ testPage.adjustTimer(testPage.currentExerciseParts[testPage.currentExerciseParts.length - 1].startIndex, true);
+ expect(document.querySelector(testPage.timerIDstring).innerHTML).toBe('1');
+ testPage.adjustTimer(1, false);
+ expect(document.querySelector(testPage.timerIDstring).innerHTML).toBe(testPage.timerValueZero);
+ });
+
+ it('should analyze results', () => {
+ testPage.isTestMode = false;
+ testPage.currentState = TestModuleState.inProgress;
+ testPage.vocService.currentTestResults = testPage.helperService.deepCopy(MockMC.testResults);
+ testPage.analyzeResults();
+ expect(testPage.results.length).toBe(3);
+ testPage.vocService.currentTestResults[21] = new TestResultMC({
+ statement: new StatementBase({result: new Result({score: new Score({scaled: 0, raw: 0})})})
+ });
+ testPage.vocService.currentTestResults[5] = testPage.vocService.currentTestResults[21];
+ testPage.isTestMode = true;
+ testPage.analyzeResults();
+ expect(testPage.results.length).toBe(4);
+ });
+
+ it('should continue to the next exercise', () => {
+ spyOn(testPage, 'showNextExercise');
+ testPage.currentExerciseIndex = 0;
+ testPage.continueToNextExercise(false);
+ expect(testPage.currentExerciseIndex).toBe(1);
+ });
+
+ it('should attempt to exit', (done) => {
+ testPage.helperService.currentPopover = null;
+ testPage.attemptExit().then(() => {
+ expect(testPage.helperService.currentPopover).toBeTruthy();
+ done();
+ });
+ });
+
+ it('should finish the current exercise', (done) => {
+ testPage.isTestMode = false;
+ spyOn(testPage, 'saveCurrentExerciseResult');
+ const continueSpy: Spy = spyOn(testPage, 'continueToNextExercise');
+ testPage.finishCurrentExercise(new XAPIevent()).then(() => {
+ expect(continueSpy).toHaveBeenCalledTimes(0);
+ testPage.isTestMode = true;
+ testPage.finishCurrentExercise(new XAPIevent()).then(() => {
+ expect(continueSpy).toHaveBeenCalledTimes(1);
+ done();
+ });
+ });
+ });
+
+ it('should get the current exercise name', () => {
+ testPage.currentExerciseIndex = 10;
+ const name: string = testPage.getCurrentExerciseName();
+ expect(name.length).toBe(15);
+ });
+
+ it('should hide the retry button', () => {
+ const iframe: HTMLIFrameElement = MockMC.addIframe(testPage.exerciseService.h5pIframeString, testPage.h5pRetryClassString);
+ const retryButton: HTMLButtonElement = iframe.contentWindow.document.querySelector(testPage.h5pRetryClassString);
+ retryButton.style.display = 'block';
+ testPage.hideRetryButton();
+ expect(retryButton.style.display).toBe('none');
+ iframe.parentNode.removeChild(iframe);
+ });
+
+ it('should initialize the timer', () => {
+ spyOn(testPage, 'updateTimer');
+ testPage.timer = null;
+ testPage.initTimer(5);
+ expect(testPage.timer).toBeTruthy();
+ clearInterval(testPage.timer);
+ });
+
+ it('should reset the test environment', () => {
+ testPage.currentExerciseIndex = 1;
+ testPage.resetTestEnvironment();
+ expect(testPage.currentExerciseIndex).toBe(0);
+ });
+
+ it('should save the current result', () => {
+ const iframe: HTMLIFrameElement = MockMC.addIframe(testPage.exerciseService.h5pIframeString, testPage.h5pShowSolutionClassString);
+ const input: HTMLInputElement = iframe.contentWindow.document.createElement('input');
+ input.setAttribute('id', testPage.h5pKnownIDstring.slice(1));
+ iframe.contentWindow.document.body.appendChild(input);
+ testPage.currentExerciseIndex = 5;
+ testPage.knownCount = [0, 0];
+ testPage.saveCurrentExerciseResult(true, new XAPIevent({data: {statement: new StatementBase()}}));
+ expect(testPage.knownCount[0]).toBe(0);
+ input.checked = true;
+ testPage.saveCurrentExerciseResult(true, new XAPIevent({data: {statement: new StatementBase()}}));
+ expect(testPage.knownCount[0]).toBe(1);
+ iframe.parentNode.removeChild(iframe);
+ });
+
+ it('should send data', (done) => {
+ testPage.wasDataSent = false;
+ testPage.vocService.currentTestResults[0] = new TestResultMC();
+ const requestSpy: Spy = spyOn(testPage.helperService, 'makePostRequest').and.callFake(() => Promise.reject());
+ testPage.sendData().then(() => {
+ }, () => {
+ expect(requestSpy).toHaveBeenCalledTimes(1);
+ requestSpy.and.returnValue(Promise.resolve());
+ testPage.sendData().then(() => {
+ expect(testPage.wasDataSent).toBe(true);
+ expect(requestSpy).toHaveBeenCalledTimes(2);
+ testPage.sendData().then(() => {
+ expect(requestSpy).toHaveBeenCalledTimes(2);
+ done();
+ });
+ });
+ });
+ });
+
+ it('should set H5P event handlers', () => {
+ const finishSpy: Spy = spyOn(testPage, 'finishCurrentExercise').and.returnValue(Promise.resolve());
+ const newDispatcher: H5PeventDispatcherMock = new H5PeventDispatcherMock();
+ spyOn(H5P.externalDispatcher, 'on').and.callFake(newDispatcher.on.bind(newDispatcher));
+ testPage.setH5PeventHandlers();
+ testPage.currentState = TestModuleState.showResults;
+ const xapiEvent: XAPIevent = new XAPIevent({
+ data: {
+ statement: new StatementBase({result: new Result(), verb: new Verb({id: configMC.xAPIverbIDanswered})})
+ }
+ });
+ newDispatcher.trigger('xAPI', xapiEvent);
+ expect(finishSpy).toHaveBeenCalledTimes(0);
+ testPage.currentState = TestModuleState.inProgress;
+ newDispatcher.trigger('xAPI', xapiEvent);
+ expect(finishSpy).toHaveBeenCalledTimes(1);
+ const inputEventSpy: Spy = spyOn(testPage, 'triggerInputEventHandler');
+ const solutionsEventSpy: Spy = spyOn(testPage, 'triggerSolutionsEventHandler');
+ testPage.currentState = TestModuleState.inProgress;
+ const domChangedEvent: any = {data: {library: testPage.h5pBlanksString}};
+ testPage.areEventHandlersSet = false;
+ newDispatcher.trigger('domChanged', domChangedEvent);
+ expect(inputEventSpy).toHaveBeenCalledTimes(1);
+ testPage.currentState = TestModuleState.showSolutions;
+ testPage.areEventHandlersSet = false;
+ newDispatcher.trigger('domChanged', domChangedEvent);
+ expect(solutionsEventSpy).toHaveBeenCalledTimes(1);
+ });
+
+ it('should show the next exercise', () => {
+ const h5pSpy: Spy = spyOn(testPage.exerciseService, 'initH5P').and.returnValue(Promise.resolve());
+ const exerciseNameSpy: Spy = spyOn(testPage, 'getCurrentExerciseName').and.returnValue(testPage.exerciseService.vocListString);
+ const resultsSpy: Spy = spyOn(testPage, 'analyzeResults');
+ const hideButtonSpy: Spy = spyOn(testPage, 'hideRetryButton');
+ const iframe: HTMLIFrameElement = MockMC.addIframe(testPage.exerciseService.h5pIframeString);
+ testPage.showNextExercise(1);
+ expect(h5pSpy).toHaveBeenCalledTimes(1);
+ testPage.vocService.currentTestResults[2] = new TestResultMC({
+ statement: new StatementBase({
+ context:
+ new Context({contextActivities: new ContextActivities({category: [new Activity({id: testPage.h5pDragTextString})]})})
+ })
+ });
+ testPage.currentExerciseIndex = 2;
+ testPage.showNextExercise(2, true);
+ expect(hideButtonSpy).toHaveBeenCalledTimes(1);
+ exerciseNameSpy.and.returnValue(testPage.nonH5Pstring);
+ testPage.showNextExercise(testPage.currentExerciseParts[testPage.currentExerciseParts.length - 1].startIndex);
+ expect(resultsSpy).toHaveBeenCalledTimes(1);
+ iframe.parentNode.removeChild(iframe);
+ });
+
+ it('should trigger the input event handler', () => {
+ const iframe: HTMLIFrameElement = MockMC.addIframe(testPage.exerciseService.h5pIframeString, testPage.h5pCheckButtonClassString);
+ const checkButton: HTMLButtonElement = iframe.contentWindow.document.querySelector(testPage.h5pCheckButtonClassString);
+ const clickSpy: Spy = spyOn(checkButton, 'click');
+ const input: HTMLInputElement = iframe.contentWindow.document.createElement('input');
+ input.classList.add(testPage.h5pTextInputClassString.slice(1));
+ iframe.contentWindow.document.body.appendChild(input);
+ testPage.triggerInputEventHandler();
+ const inputs: NodeListOf = iframe.contentWindow.document.querySelectorAll(testPage.h5pTextInputClassString);
+ const kbe: KeyboardEvent = new KeyboardEvent('keydown', {key: 'Enter'});
+ inputs[0].dispatchEvent(kbe);
+ expect(clickSpy).toHaveBeenCalledTimes(1);
+ iframe.parentNode.removeChild(iframe);
+ });
+
+ it('should trigger the solutions event handler', () => {
+ const hideButtonSpy: Spy = spyOn(testPage, 'hideRetryButton');
+ const iframe: HTMLIFrameElement = MockMC.addIframe(testPage.exerciseService.h5pIframeString, testPage.h5pCheckButtonClassString);
+ testPage.currentExerciseIndex = 0;
+ const description = 'description';
+ testPage.vocService.currentTestResults[0] = new TestResultMC({
+ statement: new StatementBase({
+ context: new Context({
+ contextActivities: new ContextActivities({category: [new Activity({id: testPage.h5pMultiChoiceString})]})
+ }),
+ object: new Activity({
+ definition: new Definition({choices: [{description: {'en-US': description}, id: 'id'}]})
+ }),
+ result: new Result({response: 'id'})
+ })
+ });
+ const ul: HTMLUListElement = iframe.contentWindow.document.createElement('ul');
+ ul.innerText = description + 's';
+ ul.classList.add(testPage.h5pAnswerClassString.slice(1));
+ iframe.contentWindow.document.body.appendChild(ul);
+ const clickSpy: Spy = spyOn(ul, 'click');
+ testPage.triggerSolutionsEventHandler();
+ expect(clickSpy).toHaveBeenCalledTimes(1);
+ testPage.vocService.currentTestResults[0].statement.context.contextActivities.category[0].id = testPage.h5pBlanksString;
+ const input: HTMLInputElement = iframe.contentWindow.document.createElement('input');
+ input.classList.add(testPage.h5pTextInputClassString.slice(1));
+ iframe.contentWindow.document.body.appendChild(input);
+ testPage.triggerSolutionsEventHandler();
+ expect(input.value).toBe(testPage.vocService.currentTestResults[0].statement.result.response);
+ testPage.vocService.currentTestResults[0].statement.context.contextActivities.category[0].id = testPage.h5pDragTextString;
+ testPage.triggerSolutionsEventHandler();
+ expect(hideButtonSpy).toHaveBeenCalledTimes(3);
+ iframe.parentNode.removeChild(iframe);
+ });
+
+ it('should update the timer', () => {
+ testPage.countDownDateTime = new Date().getTime() - 1;
+ const iframe: HTMLIFrameElement = MockMC.addIframe(testPage.exerciseService.h5pIframeString, testPage.h5pCheckButtonClassString);
+ const showNextSpy: Spy = spyOn(testPage, 'showNextExercise');
+ testPage.updateTimer();
+ expect(showNextSpy).toHaveBeenCalledTimes(1);
+ iframe.parentNode.removeChild(iframe);
});
});
diff --git a/src/app/test/test.page.ts b/src/app/test/test.page.ts
index 5f53a39..907f346 100644
--- a/src/app/test/test.page.ts
+++ b/src/app/test/test.page.ts
@@ -10,12 +10,13 @@ import {ConfirmCancelPage} from 'src/app/confirm-cancel/confirm-cancel.page';
import {ExercisePart} from 'src/app/models/exercisePart';
import Activity from 'src/app/models/xAPI/Activity';
import LanguageMap from 'src/app/models/xAPI/LanguageMap';
-import {HttpClient, HttpErrorResponse} from '@angular/common/http';
+import {HttpClient} from '@angular/common/http';
import Context from 'src/app/models/xAPI/Context';
import {TestResultMC} from 'src/app/models/testResultMC';
import {ExerciseService} from 'src/app/exercise.service';
import configMC from '../../configMC';
import {Storage} from '@ionic/storage';
+import {CorpusService} from '../corpus.service';
declare var H5P: any;
@@ -27,7 +28,6 @@ declare var H5P: any;
export class TestPage implements OnDestroy, OnInit {
Array = Array;
- HelperService = HelperService;
Object = Object;
TestModuleState = TestModuleState;
TestType = TestType;
@@ -76,17 +76,18 @@ export class TestPage implements OnDestroy, OnInit {
'fill_blanks_4', 'multi_choice_18', 'multi_choice_9']
}), new ExercisePart({exercises: ['nonH5P_2'], startIndex: 0})];
public configMC = configMC;
+ public countDownDateTime: number;
public currentExerciseIndex: number;
public currentExerciseParts: ExercisePart[];
public currentState: TestModuleState = TestModuleState.inProgress;
- public dataAlreadySentMessage: string;
- public dataSentSuccessMessage: string;
public didTimeRunOut = false;
public exerciseIndices: number[];
public finishExerciseTimeout = 200;
+ public h5pAnswerClassString = '.h5p-answer';
public h5pBlanksString = 'H5P.Blanks';
public h5pCheckButtonClassString = '.h5p-question-check-answer';
public h5pDragTextString = 'H5P.DragText';
+ public h5pKnownIDstring = '#known';
public h5pMultiChoiceString = 'H5P.MultiChoice';
public h5pRetryClassString = '.h5p-question-try-again';
public h5pRowIDstring = '#h5p-row';
@@ -102,6 +103,7 @@ export class TestPage implements OnDestroy, OnInit {
public testType: TestType;
public timer: any;
public timerIDstring = '#timer';
+ public timerValueZero = '00m00s';
public wasDataSent: boolean;
constructor(public navCtrl: NavController,
@@ -111,12 +113,12 @@ export class TestPage implements OnDestroy, OnInit {
public http: HttpClient,
public toastCtrl: ToastController,
public exerciseService: ExerciseService,
- public storage: Storage) {
- this.translate.get('DATA_SENT').subscribe(value => this.dataSentSuccessMessage = value);
- this.translate.get('DATA_ALREADY_SENT').subscribe(value => this.dataAlreadySentMessage = value);
+ public storage: Storage,
+ public helperService: HelperService,
+ public corpusService: CorpusService) {
}
- addScore(allTestIndices: number[], exercisePartIndex: number) {
+ addScore(allTestIndices: number[], exercisePartIndex: number): void {
const relevantTestIndices = allTestIndices.filter(
x => this.currentExerciseParts[exercisePartIndex].startIndex <= x && (!this
.currentExerciseParts[exercisePartIndex + 1] || x < this.currentExerciseParts[exercisePartIndex + 1].startIndex));
@@ -126,7 +128,7 @@ export class TestPage implements OnDestroy, OnInit {
this.results.push([correctlySolved.length, relevantTestIndices.length]);
}
- adjustStartIndices() {
+ adjustStartIndices(): void {
this.currentExerciseParts[0].startIndex = 0;
[...Array(this.currentExerciseParts.length).keys()].forEach((index: number) => {
if (index === 0) {
@@ -137,7 +139,7 @@ export class TestPage implements OnDestroy, OnInit {
});
}
- adjustTimer(newIndex: number, review: boolean) {
+ adjustTimer(newIndex: number, review: boolean): void {
if (!this.isTestMode) {
return;
}
@@ -153,7 +155,7 @@ export class TestPage implements OnDestroy, OnInit {
}
}
- analyzeResults() {
+ analyzeResults(): void {
this.results = [];
this.resultsBaseIndex = this.isTestMode ? 2 : 1;
const allTestIndices = Object.keys(this.vocService.currentTestResults).map(x => +x);
@@ -182,7 +184,19 @@ export class TestPage implements OnDestroy, OnInit {
this.currentState = TestModuleState.showResults;
}
- continue(isTestMode: boolean = true) {
+ attemptExit(ev: any = null): Promise {
+ return new Promise(async (resolve) => {
+ this.helperService.currentPopover = await this.popoverController.create({
+ component: ConfirmCancelPage,
+ event: ev,
+ translucent: true
+ });
+ this.helperService.currentPopover.present().then();
+ return resolve();
+ });
+ }
+
+ continueToNextExercise(isTestMode: boolean = true): void {
if (this.isTestMode && !isTestMode) {
// no pretest in learning mode
this.deleteExercisePart(1);
@@ -192,7 +206,7 @@ export class TestPage implements OnDestroy, OnInit {
this.showNextExercise(this.currentExerciseIndex, this.currentState === TestModuleState.showSolutions);
}
- deleteExercisePart(index: number) {
+ deleteExercisePart(index: number): void {
this.exerciseIndices = [];
this.currentExerciseParts.splice(index, 1);
this.adjustStartIndices();
@@ -203,32 +217,26 @@ export class TestPage implements OnDestroy, OnInit {
}, 50);
}
- async exit(ev: any = null) {
- HelperService.currentPopover = await this.popoverController.create({
- component: ConfirmCancelPage,
- event: ev,
- translucent: true
- });
- return await HelperService.currentPopover.present();
- }
-
- finishExercise(event: XAPIevent) {
- if (!this.isTestMode) {
- this.saveResult(false, event);
- return;
- }
- // hide H5P immediately so the solutions are not visible to the user
- document.querySelector(this.h5pRowIDstring).classList.add(this.hideClassString);
- // dirty hack to wait for the solutions being processed by H5P
- setTimeout(() => {
- this.saveResult(true, event);
- if (!this.didTimeRunOut) {
- this.continue();
+ finishCurrentExercise(event: XAPIevent): Promise {
+ return new Promise(resolve => {
+ if (!this.isTestMode) {
+ this.saveCurrentExerciseResult(false, event);
+ return resolve();
}
- }, this.finishExerciseTimeout);
+ // hide H5P immediately so the solutions are not visible to the user
+ document.querySelector(this.h5pRowIDstring).classList.add(this.hideClassString);
+ // dirty hack to wait for the solutions being processed by H5P
+ setTimeout(() => {
+ this.saveCurrentExerciseResult(true, event);
+ if (!this.didTimeRunOut) {
+ this.continueToNextExercise();
+ }
+ return resolve();
+ }, this.finishExerciseTimeout);
+ });
}
- getCurrentExerciseName() {
+ getCurrentExerciseName(): string {
const targetPartIndex: number = this.getCurrentExercisePartIndex();
if (!targetPartIndex) {
return '';
@@ -237,13 +245,13 @@ export class TestPage implements OnDestroy, OnInit {
.exercises[this.currentExerciseIndex - this.currentExerciseParts[targetPartIndex].startIndex];
}
- getCurrentExercisePartIndex() {
+ getCurrentExercisePartIndex(): number {
return [...Array(this.currentExerciseParts.length).keys()].find(
i => this.currentExerciseParts[i].startIndex <= this.currentExerciseIndex && (!this.currentExerciseParts[i + 1]
|| this.currentExerciseParts[i + 1].startIndex > this.currentExerciseIndex));
}
- hideRetryButton() {
+ hideRetryButton(): void {
const iframe: HTMLIFrameElement = document.querySelector(this.exerciseService.h5pIframeString);
const iframeDoc: Document = iframe.contentWindow.document;
// hide the retry button during review
@@ -253,47 +261,14 @@ export class TestPage implements OnDestroy, OnInit {
}
}
- initTimer(durationSeconds: number) {
+ initTimer(durationSeconds: number): void {
// add the new duration to countdown
- const countDownDate = new Date(new Date().getTime() + durationSeconds * 1000).getTime(); // 15 1000
+ this.countDownDateTime = new Date(new Date().getTime() + durationSeconds * 1000).getTime();
// Update the countdown every 1 second
- this.timer = setInterval(() => {
- // Get today's date and time
- const now = new Date().getTime();
- // Find the distance between now and the countdown date
- const distance = countDownDate - now;
- // Time calculations for minutes and seconds
- const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
- const seconds = Math.floor((distance % (1000 * 60)) / 1000);
- // Output the result in an element with the corresponding ID
- const timerElement = document.querySelector(this.timerIDstring);
- if (timerElement) {
- timerElement.innerHTML = minutes + 'm' + seconds + 's ';
- }
- // If the count down is over, write some text
- if (distance < 0) {
- this.removeTimer(false);
- const iframe: HTMLIFrameElement = document.querySelector(this.exerciseService.h5pIframeString);
- if (iframe) {
- const checkButton: HTMLButtonElement = iframe.contentWindow.document.body.querySelector(this.h5pCheckButtonClassString);
- if (checkButton) {
- // prevent the check button from jumping to the next exercise
- this.didTimeRunOut = true;
- checkButton.click();
- // dirty hack to wait for the XAPI handlers
- setTimeout(() => {
- this.didTimeRunOut = false;
- }, this.finishExerciseTimeout);
- }
- }
- const newIndex: number = this.currentExerciseParts[this.getCurrentExercisePartIndex() + 1].startIndex;
- this.currentExerciseIndex = newIndex;
- this.showNextExercise(newIndex);
- }
- }, 1000);
+ this.timer = setInterval(this.updateTimer, 1000);
}
- ngOnDestroy() {
+ ngOnDestroy(): void {
this.removeTimer(false);
H5P.externalDispatcher.off('xAPI');
H5P.externalDispatcher.off('domChanged');
@@ -315,7 +290,7 @@ export class TestPage implements OnDestroy, OnInit {
}
}
- randomizeTestType() {
+ randomizeTestType(): void {
this.currentExerciseParts = this.availableExerciseParts.slice();
// remove either the second last or third last exercise
const index: number = Math.random() < 0.5 ? 3 : 4;
@@ -323,20 +298,20 @@ export class TestPage implements OnDestroy, OnInit {
this.deleteExercisePart(this.currentExerciseParts.length - index);
}
- removeTimer(freeze: boolean) {
- const timerElement = document.querySelector(this.timerIDstring);
+ removeTimer(freeze: boolean): void {
+ const timerElement: HTMLSpanElement = document.querySelector(this.timerIDstring);
clearInterval(this.timer);
if (timerElement && !freeze) {
- timerElement.innerHTML = '00m00s';
+ timerElement.innerHTML = this.timerValueZero;
}
}
- reset() {
+ resetTestEnvironment(): void {
this.ngOnDestroy();
this.ngOnInit();
}
- saveResult(showSolutions: boolean, event: XAPIevent) {
+ saveCurrentExerciseResult(showSolutions: boolean, event: XAPIevent): void {
const iframe: HTMLIFrameElement = document.querySelector(this.exerciseService.h5pIframeString);
if (iframe) {
const iframeDoc: Document = iframe.contentWindow.document;
@@ -345,7 +320,7 @@ export class TestPage implements OnDestroy, OnInit {
statement: event.data.statement,
innerHTML: inner
});
- const knownCheckbox: HTMLInputElement = iframeDoc.querySelector('#known');
+ const knownCheckbox: HTMLInputElement = iframeDoc.querySelector(this.h5pKnownIDstring);
if (knownCheckbox) {
this.knownCount = [this.knownCount[0] + (knownCheckbox.checked ? 1 : 0), this.knownCount[1] + 1];
}
@@ -358,54 +333,37 @@ export class TestPage implements OnDestroy, OnInit {
}
}
- async sendData() {
- if (this.wasDataSent) {
- const toast = await this.toastCtrl.create({
- message: this.dataAlreadySentMessage,
- duration: 3000,
- position: 'top'
- });
- toast.present().then();
- return;
- }
- const fileUrl: string = configMC.backendBaseUrl + configMC.backendApiFilePath;
- HelperService.currentError = null;
- HelperService.isLoading = true;
- const formData = new FormData();
- // tslint:disable-next-line:prefer-const
- let learningResult: object = {};
- Object.keys(this.vocService.currentTestResults)
- .forEach(i => learningResult[i] = this.vocService.currentTestResults[i].statement);
- formData.append('learning_result', JSON.stringify(learningResult));
- this.http.post(fileUrl, formData).subscribe(async () => {
- HelperService.isLoading = false;
- this.wasDataSent = true;
- const toast = await this.toastCtrl.create({
- message: this.dataSentSuccessMessage,
- duration: 3000,
- position: 'top'
- });
- toast.present().then();
- }, async (error: HttpErrorResponse) => {
- HelperService.isLoading = false;
- HelperService.currentError = error;
- const toast = await this.toastCtrl.create({
- message: HelperService.generalErrorAlertMessage,
- duration: 3000,
- position: 'top'
+ sendData(): Promise {
+ return new Promise((resolve, reject) => {
+ if (this.wasDataSent) {
+ this.helperService.showToast(this.toastCtrl, this.corpusService.dataAlreadySentMessage).then();
+ return resolve();
+ }
+ const fileUrl: string = configMC.backendBaseUrl + configMC.backendApiFilePath;
+ const formData = new FormData();
+ // tslint:disable-next-line:prefer-const
+ let learningResult: object = {};
+ Object.keys(this.vocService.currentTestResults)
+ .forEach(i => learningResult[i] = this.vocService.currentTestResults[i].statement);
+ formData.append('learning_result', JSON.stringify(learningResult));
+ this.helperService.makePostRequest(this.http, this.toastCtrl, fileUrl, formData).then(async () => {
+ this.wasDataSent = true;
+ this.helperService.showToast(this.toastCtrl, this.corpusService.dataSentSuccessMessage).then();
+ return resolve();
+ }, () => {
+ return reject();
});
- toast.present().then();
});
}
- setH5PeventHandlers() {
+ setH5PeventHandlers(): void {
H5P.externalDispatcher.on('xAPI', (event: XAPIevent) => {
if (this.currentState !== TestModuleState.inProgress) {
return;
}
// results are only available when a task has been completed/answered, not in the "attempted" or "interacted" stages
if (event.data.statement.verb.id === configMC.xAPIverbIDanswered && event.data.statement.result) {
- this.finishExercise(event);
+ this.finishCurrentExercise(event).then();
}
});
H5P.externalDispatcher.on('domChanged', (event: any) => {
@@ -421,7 +379,7 @@ export class TestPage implements OnDestroy, OnInit {
});
}
- showNextExercise(newIndex: number, review: boolean = false) {
+ showNextExercise(newIndex: number, review: boolean = false): void {
this.adjustTimer(newIndex, review);
const currentExercisePart: ExercisePart = this.currentExerciseParts[this.getCurrentExercisePartIndex()];
const maxProgress: number = currentExercisePart.exercises.length;
@@ -452,15 +410,15 @@ export class TestPage implements OnDestroy, OnInit {
}
const fileName: string = currentExerciseName.split('_').slice(-1) + '_' + this.translate.currentLang + '.json';
let exerciseType = currentExerciseName.split('_').slice(0, 2).join('_');
- this.storage.set(configMC.localStorageKeyH5P, HelperService.baseUrl + '/assets/h5p/'
+ this.storage.set(configMC.localStorageKeyH5P, this.helperService.baseUrl + '/assets/h5p/'
+ exerciseType + '/content/' + fileName).then();
if (exerciseType.startsWith(this.exerciseService.vocListString)) {
exerciseType = this.exerciseService.fillBlanksString;
}
- this.exerciseService.initH5P(exerciseType);
+ this.exerciseService.initH5P(exerciseType).then();
}
- triggerInputEventHandler() {
+ triggerInputEventHandler(): void {
const iframe: HTMLIFrameElement = document.querySelector(this.exerciseService.h5pIframeString);
if (iframe) {
const inputs: NodeList = iframe.contentWindow.document.querySelectorAll(this.h5pTextInputClassString);
@@ -476,12 +434,12 @@ export class TestPage implements OnDestroy, OnInit {
checkButton.click();
}
}
- }, {passive: true});
+ }, {passive: false});
});
}
}
- triggerSolutionsEventHandler() {
+ triggerSolutionsEventHandler(): void {
const iframe: HTMLIFrameElement = document.querySelector(this.exerciseService.h5pIframeString);
if (iframe) {
if (this.vocService.currentTestResults[this.currentExerciseIndex]) {
@@ -493,7 +451,8 @@ export class TestPage implements OnDestroy, OnInit {
const oldChosen: { description: LanguageMap, id: string }[] = oldActivity.definition.choices.filter(
x => singleResponses.indexOf(x.id) > -1);
const oldCheckedStrings: string[] = oldChosen.map(x => x.description[Object.keys(x.description)[0]]);
- const newOptions: NodeList = iframe.contentWindow.document.querySelectorAll('.h5p-answer');
+ const newOptions: NodeListOf = iframe.contentWindow.document.querySelectorAll(
+ this.h5pAnswerClassString);
newOptions.forEach((newOption: HTMLUListElement) => {
if (oldCheckedStrings.indexOf(newOption.innerText.slice(0, -1)) > -1) {
newOption.click();
@@ -517,7 +476,42 @@ export class TestPage implements OnDestroy, OnInit {
}
}
- updateUI() {
+ public updateTimer(): void {
+ // Get today's date and time
+ const now = new Date().getTime();
+ // Find the distance between now and the countdown date
+ const distance = this.countDownDateTime - now;
+ // Time calculations for minutes and seconds
+ const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
+ const seconds = Math.floor((distance % (1000 * 60)) / 1000);
+ // Output the result in an element with the corresponding ID
+ const timerElement: HTMLSpanElement = document.querySelector(this.timerIDstring);
+ if (timerElement) {
+ timerElement.innerHTML = minutes + 'm' + seconds + 's ';
+ }
+ // If the count down is over, write some text
+ if (distance < 0) {
+ this.removeTimer(false);
+ const iframe: HTMLIFrameElement = document.querySelector(this.exerciseService.h5pIframeString);
+ if (iframe) {
+ const checkButton: HTMLButtonElement = iframe.contentWindow.document.body.querySelector(this.h5pCheckButtonClassString);
+ if (checkButton) {
+ // prevent the check button from jumping to the next exercise
+ this.didTimeRunOut = true;
+ checkButton.click();
+ // dirty hack to wait for the XAPI handlers
+ setTimeout(() => {
+ this.didTimeRunOut = false;
+ }, this.finishExerciseTimeout);
+ }
+ }
+ const newIndex: number = this.currentExerciseParts[this.getCurrentExercisePartIndex() + 1].startIndex;
+ this.currentExerciseIndex = newIndex;
+ this.showNextExercise(newIndex);
+ }
+ }
+
+ updateUI(): void {
// dirty hack to trigger ngIf evaluation & data bindings
(document.querySelector('#refreshUI') as HTMLLinkElement).click();
}
diff --git a/src/app/text-range/text-range.page.html b/src/app/text-range/text-range.page.html
index fee726d..6c320dd 100644
--- a/src/app/text-range/text-range.page.html
+++ b/src/app/text-range/text-range.page.html
@@ -2,12 +2,12 @@
-
+
{{cc.title}}
@@ -163,7 +163,7 @@
- {{ (HelperService.isVocabularyCheck ? "VOCABULARY_CHECK" : "SHOW_TEXT") | translate }}
+ {{ (helperService.isVocabularyCheck ? "VOCABULARY_CHECK" : "SHOW_TEXT") | translate }}
diff --git a/src/app/text-range/text-range.page.spec.ts b/src/app/text-range/text-range.page.spec.ts
index be7cf3e..59c1759 100644
--- a/src/app/text-range/text-range.page.spec.ts
+++ b/src/app/text-range/text-range.page.spec.ts
@@ -13,7 +13,7 @@ import {ReplaySubject} from 'rxjs';
import {CorpusMC} from '../models/corpusMC';
describe('TextRangePage', () => {
- let component: TextRangePage;
+ let textRangePage: TextRangePage;
let fixture: ComponentFixture;
beforeEach(async(() => {
@@ -32,16 +32,16 @@ describe('TextRangePage', () => {
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
- .compileComponents();
+ .compileComponents().then();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TextRangePage);
- component = fixture.componentInstance;
+ textRangePage = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
- expect(component).toBeTruthy();
+ expect(textRangePage).toBeTruthy();
});
});
diff --git a/src/app/text-range/text-range.page.ts b/src/app/text-range/text-range.page.ts
index bfbabef..a71a849 100644
--- a/src/app/text-range/text-range.page.ts
+++ b/src/app/text-range/text-range.page.ts
@@ -31,7 +31,6 @@ export class TextRangePage implements OnInit {
1: new BehaviorSubject(true)
};
public isTextRangeCheckRunning = false;
- HelperService = HelperService;
constructor(public navCtrl: NavController,
public corpusService: CorpusService,
@@ -229,12 +228,7 @@ export class TextRangePage implements OnInit {
this.checkTextRange(citationLabelsStart, citationLabelsEnd).then(async (isTextRangeCorrect: boolean) => {
this.isTextRangeCheckRunning = false;
if (!isTextRangeCorrect) {
- const toast = await this.toastCtrl.create({
- message: this.corpusService.invalidTextRangeString,
- duration: 3000,
- position: 'top'
- });
- toast.present().then();
+ this.helperService.showToast(this.toastCtrl, this.corpusService.invalidTextRangeString).then();
return;
}
this.corpusService.currentCorpus.pipe(take(1)).subscribe((cc: CorpusMC) => {
@@ -246,16 +240,16 @@ export class TextRangePage implements OnInit {
this.corpusService.currentUrn = newUrnBase + this.citationValuesStart.join('.') + '-' +
this.citationValuesEnd.join('.');
}
- HelperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
+ this.helperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
state.currentSetup.currentTextRange = tr;
this.corpusService.isTextRangeCorrect = true;
this.corpusService.getText().then(() => {
if (skipText) {
- HelperService.goToExerciseParametersPage(this.navCtrl).then();
- } else if (HelperService.isVocabularyCheck) {
- HelperService.goToVocabularyCheckPage(this.navCtrl).then();
+ this.helperService.goToExerciseParametersPage(this.navCtrl).then();
+ } else if (this.helperService.isVocabularyCheck) {
+ this.helperService.goToVocabularyCheckPage(this.navCtrl).then();
} else {
- HelperService.goToShowTextPage(this.navCtrl).then();
+ this.helperService.goToShowTextPage(this.navCtrl).then();
}
}, () => {
});
diff --git a/src/app/translate-testing/translate-testing.module.ts b/src/app/translate-testing/translate-testing.module.ts
index 6903b30..9b6e26c 100644
--- a/src/app/translate-testing/translate-testing.module.ts
+++ b/src/app/translate-testing/translate-testing.module.ts
@@ -1,6 +1,6 @@
import {EventEmitter, Injectable, NgModule, Pipe, PipeTransform} from '@angular/core';
import {TranslateLoader, TranslateModule, TranslatePipe, TranslateService} from '@ngx-translate/core';
-import {Observable, of} from 'rxjs';
+import {Observable, of, Subscriber} from 'rxjs';
import {
DefaultLangChangeEvent,
LangChangeEvent,
@@ -44,11 +44,17 @@ export class TranslateServiceStub {
public setDefaultLang(lang: string) {
}
+ public getDefaultLang() {
+ return 'en';
+ }
+
public getBrowserLang(): string {
return 'en';
}
- public use(lang: string) {
+ public use(lang: string): Observable {
+ this.currentLang = lang;
+ return of(true);
}
}
diff --git a/src/app/vocabulary-check/vocabulary-check.page.html b/src/app/vocabulary-check/vocabulary-check.page.html
index 6f9b2ee..231fadf 100644
--- a/src/app/vocabulary-check/vocabulary-check.page.html
+++ b/src/app/vocabulary-check/vocabulary-check.page.html
@@ -2,12 +2,12 @@
-
+
{{ 'VOCABULARY_CHECK' | translate }}
diff --git a/src/app/vocabulary-check/vocabulary-check.page.ts b/src/app/vocabulary-check/vocabulary-check.page.ts
index e269521..4462abc 100644
--- a/src/app/vocabulary-check/vocabulary-check.page.ts
+++ b/src/app/vocabulary-check/vocabulary-check.page.ts
@@ -18,7 +18,6 @@ import {TextRange} from '../models/textRange';
styleUrls: ['./vocabulary-check.page.scss'],
})
export class VocabularyCheckPage {
- HelperService = HelperService;
invalidSentenceCountString: string;
ObjectKeys = Object.keys;
VocabularyCorpus = VocabularyCorpus;
@@ -33,7 +32,8 @@ export class VocabularyCheckPage {
public translate: TranslateService,
public corpusService: CorpusService,
public http: HttpClient,
- public exerciseService: ExerciseService) {
+ public exerciseService: ExerciseService,
+ public helperService: HelperService) {
this.translate.get('INVALID_SENTENCE_COUNT').subscribe(value => this.invalidSentenceCountString = value);
this.translate.get('INVALID_QUERY_CORPUS').subscribe(value => this.invalidQueryCorpusString = value);
}
@@ -42,20 +42,10 @@ export class VocabularyCheckPage {
this.corpusService.currentCorpus.pipe(take(1)).subscribe(async (cc: CorpusMC) => {
this.corpusService.currentTextRange.pipe(take(1)).subscribe(async (tr: TextRange) => {
if (this.vocService.desiredSentenceCount < 0 || this.vocService.frequencyUpperBound < 0) {
- const toast = await this.toastCtrl.create({
- message: this.invalidSentenceCountString,
- duration: 3000,
- position: 'top'
- });
- toast.present().then();
+ this.helperService.showToast(this.toastCtrl, this.invalidSentenceCountString).then();
return;
} else if (!cc || tr.start.length === 0 || tr.end.length === 0 || !this.corpusService.isTextRangeCorrect) {
- const toast = await this.toastCtrl.create({
- message: this.invalidQueryCorpusString,
- duration: 3000,
- position: 'top'
- });
- toast.present().then();
+ this.helperService.showToast(this.toastCtrl, this.invalidQueryCorpusString).then();
return;
}
this.vocService.currentSentences = [];
diff --git a/src/app/vocabulary.service.spec.ts b/src/app/vocabulary.service.spec.ts
index 80bbdef..1b71b29 100644
--- a/src/app/vocabulary.service.spec.ts
+++ b/src/app/vocabulary.service.spec.ts
@@ -2,16 +2,24 @@ import {TestBed} from '@angular/core/testing';
import {VocabularyService} from './vocabulary.service';
import {HttpClientTestingModule} from '@angular/common/http/testing';
+import {HelperService} from './helper.service';
describe('VocabularyService', () => {
- beforeEach(() => TestBed.configureTestingModule({
- imports: [
- HttpClientTestingModule,
- ],
- }));
+ let vocabularyService: VocabularyService;
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ HttpClientTestingModule,
+ ],
+ providers: [
+ {provide: HelperService, useValue: {}},
+ ],
+ });
+ vocabularyService = TestBed.inject(VocabularyService);
+ }
+ );
it('should be created', () => {
- const service: VocabularyService = TestBed.get(VocabularyService);
- expect(service).toBeTruthy();
+ expect(vocabularyService).toBeTruthy();
});
});
diff --git a/src/app/vocabulary.service.ts b/src/app/vocabulary.service.ts
index 7e72f7e..6275868 100644
--- a/src/app/vocabulary.service.ts
+++ b/src/app/vocabulary.service.ts
@@ -22,7 +22,9 @@ export class VocabularyService {
ranking: Sentence[][] = [];
refVocMap: { [refVoc: string]: Vocabulary } = {};
- constructor(public http: HttpClient, public toastCtrl: ToastController) {
+ constructor(public http: HttpClient,
+ public toastCtrl: ToastController,
+ public helperService: HelperService) {
this.refVocMap[VocabularyCorpus.agldt] = new Vocabulary({
hasFrequencyOrder: true,
totalCount: 7182,
@@ -65,7 +67,7 @@ export class VocabularyService {
.set('frequency_upper_bound', this.frequencyUpperBound.toString())
.set('query_urn', queryUrn)
.set('show_oov', showOOV ? '1' : '0');
- HelperService.makeGetRequest(this.http, this.toastCtrl, url, params).then((ar: AnnisResponse) => {
+ this.helperService.makeGetRequest(this.http, this.toastCtrl, url, params).then((ar: AnnisResponse) => {
return resolve(ar);
}, (error: HttpErrorResponse) => {
return reject(error);
diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json
index 0f6e154..08e975e 100644
--- a/src/assets/i18n/de.json
+++ b/src/assets/i18n/de.json
@@ -6,6 +6,7 @@
"AUTHOR_SELECT": "Autor auswählen",
"AUTHOR_SHOW_ONLY_TREEBANKS": "Nur aufbereitete Texte (Haken entfernen für alle Autoren)",
"BACK": "zurück",
+ "BROWSE": "Durchsuchen...",
"BUTTON_CONTINUE": "Nächste Aufgabe",
"CALLIDUS_PROJECT": "CALLIDUS-Projekt",
"CANCEL": "Abbrechen",
@@ -206,6 +207,7 @@
"GIVEN": "Gegeben",
"HEAD_WORD": "Basiswort",
"HELP": "Hilfe",
+ "HIGHLIGHT": "Markieren",
"HOME": "Startseite",
"HOME_INTRO": "Hier dreht sich alles um Wortschatzübungen zu Originaltexten von Cicero, Ovid und Co. Unsere Devise ist: Keine Übung ohne einen Bezug zum Kontext des Wortes, wie schon der englische Linguist John Rupert Firth 1957 schrieb:",
"HOME_TITLE": "Context matters: Smart mit lateinischen Wörtern umgehen lernen!",
@@ -229,7 +231,9 @@
"MACHINA_CALLIDA_BACKEND": "Machina Callida Backend",
"MACHINA_CALLIDA_FRONTEND": "Machina Callida Frontend",
"MACHINA_CALLIDA_INTRO": "Die entwickelte Software (Open Source-Projekt auf GitLab) unterstützt eine korpusbasierte Wortschatzarbeit in der Lektürephase des Lateinunterrichts. Sie bietet Zugriff auf zahlreiche bekannte und weniger bekannte lateinische Korpora, um für ausgewählte Textstellen Übungen zu generieren. Im Folgenden werden einige wesentliche Entwicklungsschritte nachgezeichnet.",
+ "MINIMUM_WORD_FREQUENCY_COUNT": "Minimale Wortfrequenz",
"MOST_RECENT_SETUP": "Zuletzt genutzte Einstellungen",
+ "NEAREST_NEIGHBORS_COUNT": "Streuung",
"NO_ENTRY_FOUND": "Kein Eintrag verfügbar",
"NO_EXERCISES_FOUND": "Keine Übungen gefunden",
"OF": "von",
@@ -274,7 +278,9 @@
"RESEARCH_STUDIES_3": "im Lateinunterricht der älteren Fortgeschrittenen (Oberstufe) ebenfalls die Studien zu Ovid und Cicero",
"RESEARCH_STUDIES_4": "eine Testung der computergestützten Übungsformate (MC) durch Studierende der Klassischen Philologie (Dez. 2018)",
"RESULT": "Ergebnis",
- "SEARCH": "Durchsuchen...",
+ "SEARCH": "Suche",
+ "SEARCH_REGEX_MISSING": "Bitte Suchanfrage eingeben...",
+ "SEMANTICS": "Semantik",
"SHARE": "Teilen",
"SHOW_TEXT": "Text anzeigen",
"SHOW_TEXT_TITLE": "Ausgewählte Textpassage",
@@ -324,6 +330,7 @@
"TEXT_SHOW_OOV": "Unbekannte Vokabeln markieren",
"TEXT_TOO_LONG": "Text zu lang, max. Wortzahl: ",
"TEXT_WORK": "Textarbeit",
+ "TOO_MANY_SEARCH_RESULTS": "Zu viele Treffer. Bitte Auswahl einschränken...",
"TYPE": "Typ",
"UNIT_APPLICATION_TITLE": "Wortschatzarbeit am Text",
"UNIT_DATA_SECURITY": "Datenschutz: Es werden keine persönlichen Daten erhoben. Die Ergebnisse können auch nicht bis zu einzelnen Teilnehmern zurückverfolgt werden.",
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index b542828..b3b1137 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -6,6 +6,7 @@
"AUTHOR_SELECT": "Select author",
"AUTHOR_SHOW_ONLY_TREEBANKS": "High-quality texts only (uncheck for all authors)",
"BACK": "back",
+ "BROWSE": "Search...",
"BUTTON_CONTINUE": "Next exercise",
"CALLIDUS_PROJECT": "CALLIDUS Project",
"CANCEL": "Cancel",
@@ -206,6 +207,7 @@
"GIVEN": "Given",
"HEAD_WORD": "Head word",
"HELP": "Help",
+ "HIGHLIGHT": "Highlight",
"HOME": "Home",
"HOME_INTRO": "Here everything has to do with vocabulary exercises to original texts by Cicero, Ovid and Co. Our motto is: No exercise without a reference to the context of the word, as the English linguist John Rupert Firth wrote in 1957:",
"HOME_TITLE": "Context matters: Learn to use Latin words smartly!",
@@ -229,7 +231,9 @@
"MACHINA_CALLIDA_BACKEND": "Machina Callida Backend",
"MACHINA_CALLIDA_FRONTEND": "Machina Callida Frontend",
"MACHINA_CALLIDA_INTRO": "The software (open source project, GitLab) is able to create corpus-based exercises which can be used by beginners and intermediate learners as well as by teachers of Latin. Thus, it provides access to numerous known and lesser known Latin corpora. Some essential steps of development are given below.",
+ "MINIMUM_WORD_FREQUENCY_COUNT": "Minimum Word Frequency",
"MOST_RECENT_SETUP": "Most recent settings",
+ "NEAREST_NEIGHBORS_COUNT": "Dispersion",
"NO_ENTRY_FOUND": "No entry available",
"NO_EXERCISES_FOUND": "No exercises found",
"OF": "of",
@@ -274,7 +278,9 @@
"RESEARCH_STUDIES_3": "in the Latin classes of the older advanced students (upper level) also the studies to Ovid and Cicero",
"RESEARCH_STUDIES_4": "a test of the computer-aided exercise formats (MC) by students of classical philology (Dec. 2018)",
"RESULT": "Result",
- "SEARCH": "Search...",
+ "SEARCH": "Search",
+ "SEARCH_REGEX_MISSING": "Please provide search query...",
+ "SEMANTICS": "Semantics",
"SHARE": "Share",
"SHOW_TEXT": "Show text",
"SHOW_TEXT_TITLE": "Selected Text",
@@ -324,6 +330,7 @@
"TEXT_SHOW_OOV": "Highlight unknown vocabulary",
"TEXT_TOO_LONG": "Text too long, max. word count: ",
"TEXT_WORK": "Text work",
+ "TOO_MANY_SEARCH_RESULTS": "Too many hits. Please refine your query...",
"TYPE": "Type",
"UNIT_APPLICATION_TITLE": "Vocabulary work on text",
"UNIT_DATA_SECURITY": "Privacy protection: No personal data will be collected. The results can also not be traced up to individual participants.",
diff --git a/src/configMC.ts b/src/configMC.ts
index 2b86e6a..6c13b28 100644
--- a/src/configMC.ts
+++ b/src/configMC.ts
@@ -9,13 +9,16 @@ export default {
backendApiKwicPath: 'kwic',
backendApiRawtextPath: 'rawtext',
backendApiValidReffPath: 'validReff',
+ backendApiVectorNetworkPath: 'vectorNetwork',
backendApiVocabularyPath: 'vocabulary',
backendBaseApiPath: '/mc-service/mc/api/v1.0',
backendBaseUrl: '',
bambergCoreVocabularyUrl: 'https://www.ccbuchner.de/reihe-0-0/adeo-53/',
callidusProjectUrl: 'https://www.projekte.hu-berlin.de/de/callidus',
developerMailTo: 'mailto:sulzkons@hu-berlin.de',
+ excerciseTypePathMarkWords: 'mark_words',
frontendExercisePage: 'exercise',
+ h5pAssetFilePath: 'assets/dist/js/h5p-standalone-main.min.js',
intervalCorporaUpdate: 1209600000,
localStorageKeyApplicationState: 'mc/applicationState',
localStorageKeyCorpora: 'mc/corpora',
@@ -26,11 +29,29 @@ export default {
machinaCallidaFrontendUrl: 'https://scm.cms.hu-berlin.de/callidus/mc_frontend',
maxTextLength: 0,
menuId: 'mc-menu',
+ pageUrlAuthor: '/author',
+ pageUrlAuthorDetail: '/author-detail',
+ pageUrlDocExercises: '/doc-exercises',
+ pageUrlDocSoftware: '/doc-software',
+ pageUrlDocVocUnit: '/doc-voc-unit',
+ pageUrlExerciseList: '/exercise-list',
+ pageUrlExerciseParameters: '/exercise-parameters',
+ pageUrlHome: '/home',
+ pageUrlImprint: '/imprint',
+ pageUrlInfo: '/info',
+ pageUrlKwic: '/kwic',
+ pageUrlPreview: '/preview',
+ pageUrlShowText: '/show-text',
+ pageUrlSemantics: '/semantics',
+ pageUrlSources: '/sources',
+ pageUrlTest: '/test',
+ pageUrlTextRange: '/text-range',
+ pageUrlVocabularyCheck: '/vocabulary-check',
perseidsCTSapiBaseUrl: 'https://cts.perseids.org/api/cts?request=',
perseidsCTSapiGetCapabilities: 'GetCapabilities',
perseidsCTSapiGetValidReff: 'GetValidReff',
perseidsCTSapiUrnSnippet: '&urn=',
proielProjectUrl: 'https://proiel.github.io/',
vivaURN: 'urn:custom:latinLit:viva.lat',
- xAPIverbIDanswered: 'http://adlnet.gov/expapi/verbs/answered'
+ xAPIverbIDanswered: 'http://adlnet.gov/expapi/verbs/answered',
};
diff --git a/src/karma.conf.js b/src/karma.conf.js
index ba28f2a..760b1db 100644
--- a/src/karma.conf.js
+++ b/src/karma.conf.js
@@ -13,7 +13,7 @@ module.exports = function (config) {
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
- clearContext: false // leave Jasmine Spec Runner output visible in browser
+ clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
@@ -21,17 +21,16 @@ module.exports = function (config) {
fixWebpackSourcePaths: true
},
files: [
+ // "./assets/dist/js/h5p-standalone-main.js"
"./assets/dist/js/h5p-standalone-main.min.js"
],
+ mime: {'text/x-typescript': ['ts', 'tsx']},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['ChromeHeadlessNoSandbox'],
- // browserDisconnectTimeout: 10000,
- // browserDisconnectTolerance: 3,
- // browserNoActivityTimeout: 60000,
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: 'ChromeHeadless',
diff --git a/src/polyfills.ts b/src/polyfills.ts
index 11ed22f..d6ca803 100644
--- a/src/polyfills.ts
+++ b/src/polyfills.ts
@@ -19,11 +19,11 @@
*/
// polyfills for IE 11; DON'T MOVE THIS ANYWHERE ELSE... they need to be imported before everything else
-import 'core-js/es6/object';
-import 'core-js/es6/set';
-import 'core-js/es6/array';
-import 'core-js/es6/symbol';
-import 'core-js/es6/string';
+import 'core-js/es/array';
+import 'core-js/es/object';
+import 'core-js/es/set';
+import 'core-js/es/string';
+import 'core-js/es/symbol';
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
diff --git a/src/tsconfig.app.json b/src/tsconfig.app.json
index 2672bf5..9e1c00f 100644
--- a/src/tsconfig.app.json
+++ b/src/tsconfig.app.json
@@ -9,7 +9,8 @@
"test.ts",
"**/*.spec.ts",
"app/translate-testing/translate-testing.module.ts",
- "app/models/mock.ts",
+ "app/models/h5pEventDispatcherMock.ts",
+ "app/models/mockMC.ts",
"environments/environment.prod.ts",
"app/models/xAPI/IdFormattedSubStatement.ts",
"app/models/xAPI/ClientModel.ts",
@@ -34,13 +35,13 @@
"app/models/xAPI/UpRef.ts",
"app/models/xAPI/UnstoredStatementModel.ts"
]
-// "files": [
-// "main.ts",
-// "zone-flags.ts",
-// "polyfills.ts"
-// ],
-// "include": [
-// "app/models/**/*.ts",
-// "app/**/*.module.ts"
-// ]
+ // "files": [
+ // "main.ts",
+ // "zone-flags.ts",
+ // "polyfills.ts"
+ // ],
+ // "include": [
+ // "app/models/**/*.ts",
+ // "app/**/*.module.ts"
+ // ]
}
diff --git a/tsconfig.json b/tsconfig.json
index 6ec43b1..3acb028 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,12 +6,14 @@
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
- "module": "es2015", // es2015
+ "module": "esnext",
+ // es2015
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
- "target": "es5", // es5
+ "target": "es5",
+ // es5
"typeRoots": [
"node_modules/@types"
],
--
GitLab