diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5a9c61d988e097c71f99ba71d770ccc48048bb61..d0efc9eeb300ef9886c72d307271edd438fbb8f2 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 493ea8b3791d817fe16e24ace1a1eb2fe26ab8ad..dbbca1144b54d9cdde098ce0a2ad8a4f3f3e5654 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 66ea230ca881df5f30dc73f0a35f083e3db47792..fe6181bec9658ac5173245e8066493471c5b5181 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 48c5b8885124a8895a39091667fa2a8a317a97fc..7b0dac3a4bea4e87c8d62dcbabfc8f365dccd878 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 acaeaf910dc5f86f022420fdcb24e2c87c25efba..14588461f3a7c86aadf4eaf903f4b2ecf0ee7bca 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 bb13d3a2cec40d6817f066c272b4582cfcf4b621..cdc28190686103353c9fba737e5e8d7277a9b7c4 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 0de89b174c93c9b5bc59b25eff85d0579718cca7..48459e89de7da279ceb3cdfe237ea4d2296bfd4e 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 86696ff6f3879420bceaa767d5990061a1a47945..8a667b4d0af299dfe5530507cbee82eea74450ef 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 2d35e198a491e3910efc66c8e79ade4a894d01da..e0abc7abe199abc6f01a4a502e328103db5dcf66 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 df64acdfe5ad281d11bdda97bfdf8ded15e837ab..773dd87cbe4d67beed8f11046bf2988987d32293 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 4b72136116da627912ce124eba6220b4e9338e30..901610c1b837c27b222f2a4f8675abfc448d8e86 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 9c184d0338d03c499c779b03679708d45a494461..7540ad5b3607f83ce691057b5e5184159d782f09 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 7e58d11763415214b25b456ae3c96ce46422f080..bae125504500148804ea81a7281df31aec04da4d 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 d330184e8ba654d55fe5b3e3ca7476555320cd47..704739ebc478936023fe0d606933da664694a014 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 9a34e6466b7a3c73f967ead0a32cbfd7d80b7632..3294ac2653fe035441841ebcbcfd8953f26144cb 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 29c6f25e61eba37bdae9a6ee73c9512a16b1fd26..229195e22b61ec01e27f86432626beb98a8999c3 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 4e0b0353756d4950f99ccd5aac78a4a1263deb65..4daddf4fb9c112556b9005762021d39017d0795a 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 04bf9be0c07fe1aa47fac6d508fae41826cf9e42..2c49d5f07f7faaf4194bcd38b84d6b84021a24c0 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 307fbe3dae8dec7467acdba3b9491f8ce1218dd4..553dad3356cc753c1b7ac9c11a5275ebd825803b 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 114b26bb82fef98b465ef011eac555d00fac1d51..466d85c318fea98fb7423e2dfa14da096f78c2ba 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 328fc145e09fa19f32aea86b343428ca5b401544..6db091345b387d60795f0359fd5e4c5e98b11c8b 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 46b80b45e5ae3306ca395779bae7575f41fbc01d..095fb9511dc4ff3c9a6d3c2abda99a9b76008327 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 52f1d4090ba27b97c894eb02995e9012e51c4204..ee70a652340ca58a33aa7e713ac95f9f5d1aafa5 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 c106c30107ad040672ca51bded080a64c6932455..2c3678e045bc45f53faf9743dfe493a7a567c364 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 e4edf80d24a55e1e78f455a137a93c39012b3524..56ce5cad7bb9325769af258c732a989dfe40e76a 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 ee4280d064c57526af297a059a070b59a5f21435..4d4c44478bbc54abc61348f5d738222c726a7d42 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 7e09dc578cf2401140af9ccbcd32d7e3ef829639..294b842e146af54eead21e60fb62c2aff03c28e1 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 bfba0884551b42a8574439332e6c168b4debf651..b91977dbfc9c82a79d4326a3e3a9b1115a8daf74 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 fec9736b903edb547bc54311eb14dfdd0bf80d24..3d1fdf93a381b2b3a489291a380d555bf2f88c47 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 7868ef0e534ede36b16480f4e1a840b41836f7b9..8bf323b523e47f442f81c291ef0626b22f131cc5 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 13d7ccd596880d41104e7ebfc4d30cbd342127a6..96a489632bdb843d00ce257db1ada5ed5bfddaa5 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 a6cfca64d411e5a9898d783c1a15a9ca7b32df3f..1e9369b7ebe62bcf6fd882b32cfcbec252bae5f2 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 4cb112cba2268f78ee0dfe1925050cc19c28ec41..11a55f43da51a43067acb8e3c7fbbba082e01fe2 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 4f82b71ab3dee6708a0cb34fecad693ad81812de..b429901672b5570ade25dd710bf6486b7a4e2f7b 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 d3273cdb8d039dc0f4ffefb3270e543c986c5639..141d4dd615656bec6d4dc29f6a5da754dcf8134a 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 ef3bc2396191f9de365f9c411a674c8370c25f7a..dfd162bfa060a21a29966cdcd3830836d2d3b1eb 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 f4db2c3f17da049a2c3ad00a3ad78f4911c4f559..43c4e572e5eb5063c90b1967a2fab5c266d386ad 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 44ae67f73983532cb55e282857739af12c0f925f..2e6c3de077121a7308a3c1cac13652ac5a6df897 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 a71e53920b4c1786100b842bb42ae0aae299571b..8d35cc90b298fbc0ed8349f63c13823d051be7a4 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 6227decb0ba0caca425ad227e122932b2325fb4a..ccf958f4f6ea975c2740354a6aa5ba8d89f8e449 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 7bd4ef0b8a999057ab661b4104aafe53b5fddd84..f036ec570cc47dbc16c9aefaef741f8a57010508 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 262fb03627311cf7a34a64bc0c29d7702e94965f..25a7257fa5d64676d082158159989d84a6d5f64b 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 6badd47d8cde5455112b0356ae60932a56d2cbb2..fb4bfae77664fd0e0ff2b7a70d9d654f544bfc77 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 252316cadcf39037310843d87a0c4915f03ebef0..b65ef50eb0b0494aa07046a11360fd0a82181230 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 6f5bf335926c769261e2128cc0bcb6b4703f5cc0..edf32b0e8aad98d02df06d6f9e51a91b9561615b 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 8302914d2dccf9ae9be64cc88c3ff23085d9904c..08994dfa64fc778a963f3d3442a7240fe01ab0b8 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 0000000000000000000000000000000000000000..ed6ed50435727afb6d666422a51fa4636cb219c5
--- /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 630a9f9b6206b73d4355ecdc0df9c89f78de798c..0000000000000000000000000000000000000000
--- 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 0000000000000000000000000000000000000000..b6596214b7379045b9c71aece4f1e3b0010147d6
--- /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 afe4be474dd7c3970b6ee1e9a33b39e304a0703e..27800ef6f62c094bea71ffd4891d6c65fbf9b60f 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 49dfff368e1c4ecb9017c0f80a97f107b7bbae4c..8b647288bb8d579567dfb0de6930fa189a03e054 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 a5a26ebf30a5cce64e1d3154fc52bd2cee17d918..020992d8f51df9a9a82f575e08f8f192d5f01998 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 67ea87774e6d6279be19c61bc5c3590625b976ac..f59abc2dbdb49e40a5a5042171c8ba578c75e79d 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 2d4524ff79beef6b3b4e5331f2a24c6d03113d00..242663b2a3e7bfe45bfa5bd6902e0b3d53f6781a 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 8b0c45c7690bc9515eb863a63d9b1a5237ea71c9..820363b5c9eb1cc008ea700b6d0809c0f494fa11 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 5fb8a2e3cd876bbf69db1df67a5a08ef11c2076a..94f2dd1a77bd86d6c8bdc6eea729a0f72aaf7425 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 bdfa15712db051f02a39f846deadf224110de3a7..28225fe3358f6080a090dffd597da7608dd5983c 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 46a7d42f4fe041724c3c6392179158e6b994147f..57bc606246fbe30f1fc33e34854d17e9ea607e1e 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 62f6aa50736dc7c4e1620746c21e764a0eeec9b4..97c27078daa8b565e4c01f8d47d66a89e76ec8d0 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 d0beb9acfa2bd420b3662f4622ce9315bc907208..940bc438d9166ee2e5aa08728d2abea41f37cbd4 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 f84e8cd61b1cb8c21e6067e9719101317c6b6614..e6b6de79d8e1c2f096d396a6cbf4eb3f6be0b448 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 dac8fbe8ff40547d60cbe4dae57ea0a896b004e5..d13b82bc5a6abb5da7ce461d396fb1ff52b30c65 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 fcd065286ef09108c88fbec097517b14382eb182..331a3e5afcf99a5b1c29e3d6b4d2094e686e95b3 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 0000000000000000000000000000000000000000..d575b0020671fa65ae24cab1df01d0e9e3beb8e8
--- /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 0000000000000000000000000000000000000000..2a22a273e47d0f0930b08c7e217866094712bdb1
--- /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 0000000000000000000000000000000000000000..c5bdebfa70b263d04738c50787ccee561e81cfd0
--- /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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/semantics/semantics.page.spec.ts b/src/app/semantics/semantics.page.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4f5cb4a663babbfccf8e031083fac48cae798094
--- /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 0000000000000000000000000000000000000000..1463c8d998d15277f134501e83dc18bf44d9bcf5
--- /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 c27b91059db3e0120f673746ea2678486b9cee74..a439707ee16c7693b37a76969628606d018ef356 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 a4001b6472183ddb00acaa5b24efdd6c876d216e..590cbcf68331333222e1d9a38fd206c71532310c 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 935bb700040c43ccfc8c0d60a2d9cdbce3dc2dfc..dc87fee98a7480299c00e710937af79deea276eb 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 1411c6a617ec8faa25a44944a2a3003dbe4e2442..11d0798858aa8b99f130ca792133c4792e076fbd 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 7c2be121f292810c733abbaf93a5940b2c404054..bdae6e7ccf586b810e5d08ab0d5717b3bece0fd9 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 cd93b2e88b0ccbcdde476c2da3e9ed42ff9f0051..6306358905c8026ba75c7856d3bd99c24feb83bc 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 f867601193c327c1a1221f5486e4f3f8bd115733..7a1d5e6d7158a310b2f7ae6249f59ccfb532c3b7 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 5f53a395ffa060308d428cb463bd0f1e1043742e..907f34622a746e2dd2243f2fd1c8ac8a942ed743 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 fee726de55b37c6c82277100e6c42284bd6976ac..6c320ddd1742417065e3d5c0073bf1dbecc54b79 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 be7cf3eb053ce09781a2cbd35f7e7c3df3b6cc2b..59c17594f08b8720c42a0fcb0b5d1bfa4adb90ee 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 bfbabef4bdbce875ab8a3a90f6b553f2667f048e..a71a8499d6385538b99998d7d9132f2abbc9b634 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 6903b30829b4aa7b0d58d35f425dc3a76fe38ca3..9b6e26c5c7b23ccd3c8be2762326c79db08252c4 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 6f9b2ee8df7f011bed045c7866e6525df01c6749..231fadf2e216a6d9e4846a1b86ebf107c86fc6e2 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 e269521daaf6873e1b2ae372a0da20adba16213a..4462abcdbbe0bba521f615b00020b80afb716fcf 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 80bbdef8d5f9c73062587160f3f6fdf045f9f466..1b71b29fe8b037b8aba3d955db241a8b6f102892 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 7e72f7e1d05c4ba60eebc128c630ff3565ddc654..6275868d285c0d2ec222b4674fd6f5c62e495213 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 0f6e154456214a8ca9369de6bcafd95f922f2573..08e975eadafcb3e5cb3cd021c5eba7b4452819ca 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 b542828ca3d7e1a7e5f003a426ce4970d698582a..b3b11371a0ab57f868ac20a4094657107e447935 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 2b86e6a63cb7e735415a51261320ecf5ed8f1964..6c13b2872bcfe824faba4a82960fb0609d47f26a 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 ba28f2a1b36240e7b33679f758dddae52446e1d9..760b1dba28afdf391fa23e75339074766da5a46e 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 11ed22fc4ff16ef9cd12ba7b9fe0eea5bef70b87..d6ca8037b54aadc2380adf7d135e134f510d2dec 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 2672bf5985b0339e816dd61686ccca697b063cd5..9e1c00fcec76e4e5bfce7601a58c1619fb014231 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 6ec43b170f8734bc0d2dafb350f50191f7342a20..3acb028930a349800eb247924aa8d6e210807bcc 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"
],