Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b25b8cc805 | ||
|
|
8141a01ef7 | ||
|
|
a2f10857e2 | ||
|
|
aea04e5f7c | ||
|
|
60c80611ea | ||
|
|
b1ed3ce55f | ||
|
|
040ed2701c | ||
|
|
00d8e551db | ||
|
|
3e9c99f5f8 | ||
|
|
02bdc4cf04 | ||
|
|
5c43965f0b | ||
|
|
b2376ecc30 | ||
|
|
aeaa6deeb4 |
1
.github/workflows/quality-checks.yaml
vendored
1
.github/workflows/quality-checks.yaml
vendored
@@ -13,6 +13,7 @@ jobs:
|
||||
- npm run lint:md
|
||||
- npm run lint:md:relative-urls
|
||||
- npm run lint:md:consistency
|
||||
fail-fast: false # So it continues with other commands if one fails
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
1
.github/workflows/test.yaml
vendored
1
.github/workflows/test.yaml
vendored
@@ -7,6 +7,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos, ubuntu, windows]
|
||||
fail-fast: false # So it still runs on other OSes if one of them fails
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
steps:
|
||||
-
|
||||
|
||||
15
CHANGELOG.md
15
CHANGELOG.md
@@ -1,5 +1,20 @@
|
||||
# Changelog
|
||||
|
||||
## 0.10.1 (2021-03-25)
|
||||
|
||||
* refactor script compilation to make it easy to add new expressions #41 #53 | [646db90](https://github.com/undergroundwires/privacy.sexy/commit/646db9058541cebd0af437554de04fdc6bb63a6e)
|
||||
* restructure presentation layer | [f3c7413](https://github.com/undergroundwires/privacy.sexy/commit/f3c7413f529be4a00dba7b0ab23904b48ea13a35)
|
||||
* fix a test where "it" is not used inside "describe" | [1a5f920](https://github.com/undergroundwires/privacy.sexy/commit/1a5f92021f7423cd039f8f5326cd6f99b355c962)
|
||||
* bump dependencies to latest | [1f515e7](https://github.com/undergroundwires/privacy.sexy/commit/1f515e7be525291c960ccb71db05312db6da53f5)
|
||||
* fix throttle function not being able to run with argument(s) | [1935db1](https://github.com/undergroundwires/privacy.sexy/commit/1935db10192051401ab00ca2cd767955d0d3b866)
|
||||
* fix fs module hanging not allowing code to run | [5f527a0](https://github.com/undergroundwires/privacy.sexy/commit/5f527a00cf225d3e74b3f6577d6e2456e919de24)
|
||||
* refactor all modals to use same dialog component | [6f46cdb](https://github.com/undergroundwires/privacy.sexy/commit/6f46cdb4ed49a8941c6c0dde5c5e2a816c06daef)
|
||||
* fix safari cleanup scripts that are not working on modern versions | [05932c5](https://github.com/undergroundwires/privacy.sexy/commit/05932c5a36446d551c5bc811165e3295fbe15e3f)
|
||||
* refactor features to use shared functions #41 | [ac2249f](https://github.com/undergroundwires/privacy.sexy/commit/ac2249f25664827d8a6d2c7ebd659ccf126b0cde)
|
||||
* increase performance by polyfilling ResizeObserver only if required | [448e378](https://github.com/undergroundwires/privacy.sexy/commit/448e378dc4501f9de69af63634c87d0e5060bf52)
|
||||
|
||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.10.0...0.10.1)
|
||||
|
||||
## 0.10.0 (2021-03-02)
|
||||
|
||||
* allow functions to call other functions #53 | [7661575](https://github.com/undergroundwires/privacy.sexy/commit/7661575573c6d3e8f4bc28bfa7a124a764c72ef9)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
- Online version at [https://privacy.sexy](https://privacy.sexy)
|
||||
- 💡 No need to run any compiled software on your computer.
|
||||
- Alternatively download offline version for [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.10.0/privacy.sexy-Setup-0.10.0.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.10.0/privacy.sexy-0.10.0.dmg) or [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.10.0/privacy.sexy-0.10.0.AppImage).
|
||||
- Alternatively download offline version for [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.10.1/privacy.sexy-Setup-0.10.1.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.10.1/privacy.sexy-0.10.1.dmg) or [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.10.1/privacy.sexy-0.10.1.AppImage).
|
||||
- 💡 Single click to execute your script.
|
||||
- ❗ Come back regularly to apply latest version for stronger privacy and security.
|
||||
|
||||
@@ -53,8 +53,8 @@
|
||||
- Development: `npm run serve` to compile & hot-reload for development.
|
||||
- Production: `npm run build` to prepare files for distribution.
|
||||
- Or run using Docker:
|
||||
1. Build: `docker build -t undergroundwires/privacy.sexy:0.10.0 .`
|
||||
2. Run: `docker run -it -p 8080:80 --rm --name privacy.sexy-0.10.0 undergroundwires/privacy.sexy:0.10.0`
|
||||
1. Build: `docker build -t undergroundwires/privacy.sexy:0.10.1 .`
|
||||
2. Run: `docker run -it -p 8080:80 --rm --name privacy.sexy-0.10.1 undergroundwires/privacy.sexy:0.10.1`
|
||||
|
||||
## Architecture overview
|
||||
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
- It's mainly responsible for
|
||||
- creating and event based [application state](#application-state)
|
||||
- [parsing](#parsing) and [compiling](#compiling) [application data](#application-data)
|
||||
- Consumed by [presentation layer](./presentation.md)
|
||||
|
||||
## Structure
|
||||
|
||||
- [`/src/` **`application/`**](./../src/application/): Contains all application related code.
|
||||
- [**`collections/`**](./../src/application/collections/): Holds [collection files](./collection-files.md)
|
||||
- [**`Common/`**](./../src/application/Common/): Contains common functionality that is shared in application layer.
|
||||
- `..`: other classes are categorized using folders-by-feature structure
|
||||
|
||||
## Application state
|
||||
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
# Unit tests
|
||||
# Tests
|
||||
|
||||
- 💡 You can use path/module alias `@/tests` in import statements.
|
||||
|
||||
## Unit tests
|
||||
|
||||
- Unit tests are defined in [`./tests`](./../tests)
|
||||
- They follow same folder structure as [`./src`](./../src)
|
||||
|
||||
## Naming
|
||||
### Naming
|
||||
|
||||
- Each test suite first describe the system under test
|
||||
- E.g. tests for class `Application` is categorized under `Application`
|
||||
- Tests for specific methods are categorized under method name (if applicable)
|
||||
- E.g. test for `run()` is categorized under `run`
|
||||
|
||||
## Act, arrange, assert
|
||||
### Act, arrange, assert
|
||||
|
||||
- Tests use act, arrange and assert (AAA) pattern when applicable
|
||||
- **Arrange**
|
||||
@@ -23,7 +27,7 @@
|
||||
- Should elicit some sort of response
|
||||
- Starts with comment line `// assert`
|
||||
|
||||
## Stubs
|
||||
### Stubs
|
||||
|
||||
- Stubs are defined in [`./tests/stubs`](./../tests/unit/stubs)
|
||||
- They implement dummy behavior to be functional
|
||||
|
||||
81
package-lock.json
generated
81
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "privacy.sexy",
|
||||
"version": "0.10.0",
|
||||
"version": "0.10.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -1681,8 +1681,7 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"@types/q": {
|
||||
"version": "1.5.4",
|
||||
@@ -4109,8 +4108,7 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"camel-case": {
|
||||
"version": "3.0.0",
|
||||
@@ -5106,7 +5104,6 @@
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
|
||||
"integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.1.0",
|
||||
@@ -5565,8 +5562,7 @@
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
|
||||
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"default-gateway": {
|
||||
"version": "5.0.5",
|
||||
@@ -7258,7 +7254,6 @@
|
||||
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.1.tgz",
|
||||
"integrity": "sha512-SVi+ZAQOGbtAsUWrZvGzz38ga2YqjWvca1pXQFUArIVXqli0lLoDQ8uS0wg0kSpcwpZmaW5jVCZXQebkyUQSsw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.8.3",
|
||||
"@types/json-schema": "^7.0.5",
|
||||
@@ -7278,7 +7273,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
@@ -7288,7 +7282,6 @@
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
@@ -7299,7 +7292,6 @@
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
@@ -7308,22 +7300,19 @@
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
@@ -7333,7 +7322,6 @@
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
|
||||
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.4",
|
||||
"ajv": "^6.12.2",
|
||||
@@ -7341,11 +7329,10 @@
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
|
||||
"integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
|
||||
"version": "7.3.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
@@ -7355,7 +7342,6 @@
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
@@ -7364,8 +7350,7 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -7513,8 +7498,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.1.tgz",
|
||||
"integrity": "sha512-fcSa+wyTqZa46iWweI7/ZiUfegOZl0SG8+dltIwFXo7+zYU9J9kpS3NB6pZcSlJdhvIwp81Adx2XhZorncxiaA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"fs-write-stream-atomic": {
|
||||
"version": "1.0.10",
|
||||
@@ -8296,7 +8280,6 @@
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"parent-module": "^1.0.0",
|
||||
"resolve-from": "^4.0.0"
|
||||
@@ -9844,7 +9827,6 @@
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.0.tgz",
|
||||
"integrity": "sha512-f/xxz2TpdKv6uDn6GtHee8ivFyxwxmPuXatBb1FBwxYNuVpbM3k/Y1Z+vC0mH/dIXXrukYfe3qe5J32Dfjg93A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"fs-monkey": "1.0.1"
|
||||
}
|
||||
@@ -10812,9 +10794,9 @@
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
|
||||
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz",
|
||||
"integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
@@ -11456,7 +11438,6 @@
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"callsites": "^3.0.0"
|
||||
}
|
||||
@@ -13274,8 +13255,7 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"resolve-url": {
|
||||
"version": "0.2.1",
|
||||
@@ -16424,11 +16404,10 @@
|
||||
}
|
||||
},
|
||||
"vue-loader-v16": {
|
||||
"version": "npm:vue-loader@16.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.1.2.tgz",
|
||||
"integrity": "sha512-8QTxh+Fd+HB6fiL52iEVLKqE9N1JSlMXLR92Ijm6g8PZrwIxckgpqjPDWRP5TWxdiPaHR+alUWsnu1ShQOwt+Q==",
|
||||
"version": "npm:vue-loader@16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.2.0.tgz",
|
||||
"integrity": "sha512-TitGhqSQ61RJljMmhIGvfWzJ2zk9m1Qug049Ugml6QP3t0e95o0XJjk29roNEiPKJQBEi8Ord5hFuSuELzSp8Q==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"chalk": "^4.1.0",
|
||||
"hash-sum": "^2.0.0",
|
||||
@@ -16440,7 +16419,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
@@ -16450,7 +16428,6 @@
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
@@ -16461,7 +16438,6 @@
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
@@ -16470,29 +16446,25 @@
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"emojis-list": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
||||
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"json5": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
|
||||
"integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
@@ -16502,7 +16474,6 @@
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
|
||||
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
@@ -16514,7 +16485,6 @@
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
@@ -17397,11 +17367,10 @@
|
||||
"dev": true
|
||||
},
|
||||
"yaml": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz",
|
||||
"integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
|
||||
"dev": true
|
||||
},
|
||||
"yaml-lint": {
|
||||
"version": "1.2.4",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "privacy.sexy",
|
||||
"version": "0.10.0",
|
||||
"version": "0.10.1",
|
||||
"private": true,
|
||||
"description": "Enforce privacy & security best-practices on Windows and macOS, because privacy is sexy 🍑🍆",
|
||||
"author": "undergroundwires",
|
||||
|
||||
21
src/application/Common/Array.ts
Normal file
21
src/application/Common/Array.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
// Compares to Array<T> objects for equality, ignoring order
|
||||
export function scrambledEqual<T>(array1: readonly T[], array2: readonly T[]) {
|
||||
if (!array1) { throw new Error('undefined first array'); }
|
||||
if (!array2) { throw new Error('undefined second array'); }
|
||||
const sortedArray1 = sort(array1);
|
||||
const sortedArray2 = sort(array2);
|
||||
return sequenceEqual(sortedArray1, sortedArray2);
|
||||
function sort(array: readonly T[]) {
|
||||
return array.slice().sort();
|
||||
}
|
||||
}
|
||||
|
||||
// Compares to Array<T> objects for equality in same order
|
||||
export function sequenceEqual<T>(array1: readonly T[], array2: readonly T[]) {
|
||||
if (!array1) { throw new Error('undefined first array'); }
|
||||
if (!array2) { throw new Error('undefined second array'); }
|
||||
if (array1.length !== array2.length) {
|
||||
return false;
|
||||
}
|
||||
return array1.every((val, index) => val === array2[index]);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// Because we cannot do "T extends enum" 😞 https://github.com/microsoft/TypeScript/issues/30611
|
||||
type EnumType = number | string;
|
||||
type EnumVariable<T extends EnumType, TEnumValue extends EnumType> = { [key in T]: TEnumValue };
|
||||
export type EnumType = number | string;
|
||||
export type EnumVariable<T extends EnumType, TEnumValue extends EnumType> = { [key in T]: TEnumValue };
|
||||
|
||||
export interface IEnumParser<TEnum> {
|
||||
parseEnum(value: string, propertyName: string): TEnum;
|
||||
@@ -41,3 +41,14 @@ export function getEnumValues<T extends EnumType, TEnumValue extends EnumType>(
|
||||
return getEnumNames(enumVariable)
|
||||
.map((level) => enumVariable[level]) as TEnumValue[];
|
||||
}
|
||||
|
||||
export function assertInRange<T extends EnumType, TEnumValue extends EnumType>(
|
||||
value: TEnumValue,
|
||||
enumVariable: EnumVariable<T, TEnumValue>) {
|
||||
if (value === undefined) {
|
||||
throw new Error('undefined enum value');
|
||||
}
|
||||
if (!(value in enumVariable)) {
|
||||
throw new RangeError(`enum value "${value}" is out of range`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
|
||||
export interface IScriptingLanguageFactory<T> {
|
||||
create(language: ScriptingLanguage): T;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { IScriptingLanguageFactory } from './IScriptingLanguageFactory';
|
||||
import { assertInRange } from '@/application/Common/Enum';
|
||||
|
||||
type Getter<T> = () => T;
|
||||
|
||||
export abstract class ScriptingLanguageFactory<T> implements IScriptingLanguageFactory<T> {
|
||||
private readonly getters = new Map<ScriptingLanguage, Getter<T>>();
|
||||
|
||||
public create(language: ScriptingLanguage): T {
|
||||
assertInRange(language, ScriptingLanguage);
|
||||
if (!this.getters.has(language)) {
|
||||
throw new RangeError(`unknown language: "${ScriptingLanguage[language]}"`);
|
||||
}
|
||||
const getter = this.getters.get(language);
|
||||
const instance = getter();
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected registerGetter(language: ScriptingLanguage, getter: Getter<T>) {
|
||||
assertInRange(language, ScriptingLanguage);
|
||||
if (!getter) {
|
||||
throw new Error('undefined getter');
|
||||
}
|
||||
if (this.getters.has(language)) {
|
||||
throw new Error(`${ScriptingLanguage[language]} is already registered`);
|
||||
}
|
||||
this.getters.set(language, getter);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { IApplication } from '@/domain/IApplication';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import { EventSource } from '@/infrastructure/Events/EventSource';
|
||||
import { assertInRange } from '@/application/Common/Enum';
|
||||
|
||||
type StateMachine = Map<OperatingSystem, ICategoryCollectionState>;
|
||||
|
||||
@@ -22,7 +23,7 @@ export class ApplicationContext implements IApplicationContext {
|
||||
public readonly app: IApplication,
|
||||
initialContext: OperatingSystem) {
|
||||
validateApp(app);
|
||||
validateOs(initialContext);
|
||||
assertInRange(initialContext, OperatingSystem);
|
||||
this.states = initializeStates(app);
|
||||
this.changeContext(initialContext);
|
||||
}
|
||||
@@ -50,18 +51,6 @@ function validateApp(app: IApplication) {
|
||||
}
|
||||
}
|
||||
|
||||
function validateOs(os: OperatingSystem) {
|
||||
if (os === undefined) {
|
||||
throw new Error('undefined os');
|
||||
}
|
||||
if (os === OperatingSystem.Unknown) {
|
||||
throw new Error('unknown os');
|
||||
}
|
||||
if (!(os in OperatingSystem)) {
|
||||
throw new Error(`os "${os}" is out of range`);
|
||||
}
|
||||
}
|
||||
|
||||
function initializeStates(app: IApplication): StateMachine {
|
||||
const machine = new Map<OperatingSystem, ICategoryCollectionState>();
|
||||
for (const collection of app.collections) {
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { ScriptingLanguageFactory } from '@/application/Common/ScriptingLanguage/ScriptingLanguageFactory';
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { ICodeBuilder } from './ICodeBuilder';
|
||||
import { ICodeBuilderFactory } from './ICodeBuilderFactory';
|
||||
import { BatchBuilder } from './Languages/BatchBuilder';
|
||||
import { ShellBuilder } from './Languages/ShellBuilder';
|
||||
import { ICodeBuilderFactory } from './ICodeBuilderFactory';
|
||||
|
||||
export class CodeBuilderFactory implements ICodeBuilderFactory {
|
||||
public create(language: ScriptingLanguage): ICodeBuilder {
|
||||
switch (language) {
|
||||
case ScriptingLanguage.shellscript: return new ShellBuilder();
|
||||
case ScriptingLanguage.batchfile: return new BatchBuilder();
|
||||
default: throw new RangeError(`unknown language: "${ScriptingLanguage[language]}"`);
|
||||
}
|
||||
export class CodeBuilderFactory extends ScriptingLanguageFactory<ICodeBuilder> implements ICodeBuilderFactory {
|
||||
constructor() {
|
||||
super();
|
||||
this.registerGetter(ScriptingLanguage.shellscript, () => new ShellBuilder());
|
||||
this.registerGetter(ScriptingLanguage.batchfile, () => new BatchBuilder());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { ICodeBuilder } from './ICodeBuilder';
|
||||
import { IScriptingLanguageFactory } from '@/application/Common/ScriptingLanguage/IScriptingLanguageFactory';
|
||||
|
||||
export interface ICodeBuilderFactory {
|
||||
create(language: ScriptingLanguage): ICodeBuilder;
|
||||
export interface ICodeBuilderFactory extends IScriptingLanguageFactory<ICodeBuilder> {
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||
export interface IUserSelection {
|
||||
readonly changed: IEventSource<ReadonlyArray<SelectedScript>>;
|
||||
readonly selectedScripts: ReadonlyArray<SelectedScript>;
|
||||
readonly totalSelected: number;
|
||||
areAllSelected(category: ICategory): boolean;
|
||||
isAnySelected(category: ICategory): boolean;
|
||||
removeAllInCategory(categoryId: number): void;
|
||||
|
||||
@@ -101,10 +101,6 @@ export class UserSelection implements IUserSelection {
|
||||
return this.scripts.getItems();
|
||||
}
|
||||
|
||||
public get totalSelected(): number {
|
||||
return this.scripts.getItems().length;
|
||||
}
|
||||
|
||||
public selectAll(): void {
|
||||
for (const script of this.collection.getAllScripts()) {
|
||||
if (!this.scripts.exists(script.id)) {
|
||||
|
||||
@@ -4,17 +4,17 @@ import { IBrowserOsDetector } from './IBrowserOsDetector';
|
||||
|
||||
export class BrowserOsDetector implements IBrowserOsDetector {
|
||||
private readonly detectors = BrowserDetectors;
|
||||
public detect(userAgent: string): OperatingSystem {
|
||||
public detect(userAgent: string): OperatingSystem | undefined {
|
||||
if (!userAgent) {
|
||||
return OperatingSystem.Unknown;
|
||||
return undefined;
|
||||
}
|
||||
for (const detector of this.detectors) {
|
||||
const os = detector.detect(userAgent);
|
||||
if (os !== OperatingSystem.Unknown) {
|
||||
if (os !== undefined) {
|
||||
return os;
|
||||
}
|
||||
}
|
||||
return OperatingSystem.Unknown;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@ export class DetectorBuilder {
|
||||
throw new Error('User agent is null or undefined');
|
||||
}
|
||||
if (this.existingPartsInUserAgent.some((part) => !userAgent.includes(part))) {
|
||||
return OperatingSystem.Unknown;
|
||||
return undefined;
|
||||
}
|
||||
if (this.notExistingPartsInUserAgent.some((part) => userAgent.includes(part))) {
|
||||
return OperatingSystem.Unknown;
|
||||
return undefined;
|
||||
}
|
||||
return this.os;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
|
||||
export interface IBrowserOsDetector {
|
||||
detect(userAgent: string): OperatingSystem;
|
||||
detect(userAgent: string): OperatingSystem | undefined;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ function getProcessPlatform(variables: IEnvironmentVariables): string {
|
||||
return variables.process.platform;
|
||||
}
|
||||
|
||||
function getDesktopOsType(processPlatform: string): OperatingSystem {
|
||||
function getDesktopOsType(processPlatform: string): OperatingSystem | undefined {
|
||||
// https://nodejs.org/api/process.html#process_process_platform
|
||||
if (processPlatform === 'darwin') {
|
||||
return OperatingSystem.macOS;
|
||||
@@ -53,7 +53,7 @@ function getDesktopOsType(processPlatform: string): OperatingSystem {
|
||||
} else if (processPlatform === 'linux') {
|
||||
return OperatingSystem.Linux;
|
||||
}
|
||||
return OperatingSystem.Unknown;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isDesktop(variables: IEnvironmentVariables): boolean {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||
import { FunctionData } from 'js-yaml-loader!*';
|
||||
import { FunctionData } from 'js-yaml-loader!@/*';
|
||||
import { IScriptCompiler } from './Compiler/IScriptCompiler';
|
||||
import { ScriptCompiler } from './Compiler/ScriptCompiler';
|
||||
import { ICategoryCollectionParseContext } from './ICategoryCollectionParseContext';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FunctionData, InstructionHolder } from 'js-yaml-loader!*';
|
||||
import { FunctionData, InstructionHolder } from 'js-yaml-loader!@/*';
|
||||
import { SharedFunction } from './SharedFunction';
|
||||
import { SharedFunctionCollection } from './SharedFunctionCollection';
|
||||
import { ISharedFunctionCollection } from './ISharedFunctionCollection';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FunctionData } from 'js-yaml-loader!*';
|
||||
import { FunctionData } from 'js-yaml-loader!@/*';
|
||||
import { ISharedFunctionCollection } from './ISharedFunctionCollection';
|
||||
|
||||
export interface IFunctionCompiler {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { ISharedFunction } from './ISharedFunction';
|
||||
|
||||
export class SharedFunction implements ISharedFunction {
|
||||
public readonly parameters: readonly string[];
|
||||
constructor(
|
||||
public readonly name: string,
|
||||
public readonly parameters: readonly string[],
|
||||
parameters: readonly string[],
|
||||
public readonly code: string,
|
||||
public readonly revertCode: string,
|
||||
) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FunctionCallData, FunctionCallParametersData, FunctionData, ScriptFunctionCallData } from 'js-yaml-loader!*';
|
||||
import { FunctionCallData, FunctionCallParametersData, FunctionData, ScriptFunctionCallData } from 'js-yaml-loader!@/*';
|
||||
import { ICompiledCode } from './ICompiledCode';
|
||||
import { ISharedFunctionCollection } from '../Function/ISharedFunctionCollection';
|
||||
import { IFunctionCallCompiler } from './IFunctionCallCompiler';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ScriptFunctionCallData } from 'js-yaml-loader!*';
|
||||
import { ScriptFunctionCallData } from 'js-yaml-loader!@/*';
|
||||
import { ICompiledCode } from './ICompiledCode';
|
||||
import { ISharedFunctionCollection } from '../Function/ISharedFunctionCollection';
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { IScriptingLanguageFactory } from '@/application/Common/ScriptingLanguage/IScriptingLanguageFactory';
|
||||
|
||||
export interface ISyntaxFactory {
|
||||
create(language: ScriptingLanguage): ILanguageSyntax;
|
||||
export interface ISyntaxFactory extends IScriptingLanguageFactory<ILanguageSyntax> {
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { ISyntaxFactory } from './ISyntaxFactory';
|
||||
import { ScriptingLanguageFactory } from '@/application/Common/ScriptingLanguage/ScriptingLanguageFactory';
|
||||
import { BatchFileSyntax } from './BatchFileSyntax';
|
||||
import { ShellScriptSyntax } from './ShellScriptSyntax';
|
||||
import { ISyntaxFactory } from './ISyntaxFactory';
|
||||
|
||||
export class SyntaxFactory implements ISyntaxFactory {
|
||||
public create(language: ScriptingLanguage): ILanguageSyntax {
|
||||
switch (language) {
|
||||
case ScriptingLanguage.batchfile: return new BatchFileSyntax();
|
||||
case ScriptingLanguage.shellscript: return new ShellScriptSyntax();
|
||||
default: throw new RangeError(`unknown language: "${ScriptingLanguage[language]}"`);
|
||||
}
|
||||
export class SyntaxFactory extends ScriptingLanguageFactory<ILanguageSyntax> implements ISyntaxFactory {
|
||||
constructor() {
|
||||
super();
|
||||
this.registerGetter(ScriptingLanguage.batchfile, () => new BatchFileSyntax());
|
||||
this.registerGetter(ScriptingLanguage.shellscript, () => new ShellScriptSyntax());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
declare module 'js-yaml-loader!*' {
|
||||
declare module 'js-yaml-loader!@/*' {
|
||||
export interface CollectionData {
|
||||
readonly os: string;
|
||||
readonly scripting: ScriptingDefinitionData;
|
||||
|
||||
@@ -1248,25 +1248,52 @@ actions:
|
||||
-
|
||||
name: Disable ad customization with Advertising ID
|
||||
recommend: standard
|
||||
docs: https://docs.microsoft.com/en-us/windows/privacy/manage-connections-from-windows-operating-system-components-to-microsoft-services#181-general
|
||||
code: |-
|
||||
reg add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\AdvertisingInfo" /v "Enabled" /t REG_DWORD /d 0 /f
|
||||
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\AdvertisingInfo" /v "DisabledByGroupPolicy" /t REG_DWORD /d 1 /f
|
||||
reg add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\AdvertisingInfo" /v "Enabled" /t REG_DWORD /d "0" /f
|
||||
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\AdvertisingInfo" /v "DisabledByGroupPolicy" /t REG_DWORD /d "1" /f
|
||||
revertCode: |-
|
||||
reg add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\AdvertisingInfo" /v "Enabled" /t REG_DWORD /d "1" /f
|
||||
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\AdvertisingInfo" /v "DisabledByGroupPolicy" /t REG_DWORD /d "0" /f
|
||||
-
|
||||
category: Disable cloud-based tips and ads
|
||||
children:
|
||||
-
|
||||
name: Disable Windows Tips
|
||||
recommend: standard
|
||||
docs: https://admx.help/?Category=Windows_10_2016&Policy=Microsoft.Policies.CloudContent::DisableSoftLanding
|
||||
code: reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\CloudContent" /v "DisableSoftLanding" /t REG_DWORD /d "1" /f
|
||||
revertCode: reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\CloudContent" /v "DisableSoftLanding" /t REG_DWORD /d "0" /f
|
||||
-
|
||||
name: Disable Windows Spotlight (random wallpaper on lock screen)
|
||||
recommend: standard
|
||||
docs:
|
||||
- https://docs.microsoft.com/en-us/windows/configuration/windows-spotlight
|
||||
- https://docs.microsoft.com/en-us/windows/privacy/manage-connections-from-windows-operating-system-components-to-microsoft-services#25-windows-spotlight
|
||||
code: reg add "HKLM\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableWindowsSpotlightFeatures" /t "REG_DWORD" /d "1" /f
|
||||
revertCode: reg add "HKLM\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableWindowsSpotlightFeatures" /t "REG_DWORD" /d "0" /f
|
||||
-
|
||||
name: Disable Microsoft consumer experiences
|
||||
recommend: standard
|
||||
docs:
|
||||
- https://www.stigviewer.com/stig/windows_10/2018-04-06/finding/V-71771
|
||||
- https://admx.help/?Category=Windows_10_2016&Policy=Microsoft.Policies.CloudContent::DisableWindowsConsumerFeatures
|
||||
- https://docs.microsoft.com/en-us/windows/privacy/manage-connections-from-windows-operating-system-components-to-microsoft-services#1816-feedback--diagnostics
|
||||
code: reg add "HKLM\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableWindowsConsumerFeatures" /t "REG_DWORD" /d "1" /f
|
||||
revertCode: reg add "HKLM\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableWindowsConsumerFeatures" /t "REG_DWORD" /d "0" /f
|
||||
-
|
||||
name: Disable targeted tips
|
||||
recommend: standard
|
||||
code: |-
|
||||
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\CloudContent" /v "DisableSoftLanding" /t REG_DWORD /d 1 /f
|
||||
reg add "HKLM\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableWindowsSpotlightFeatures" /t "REG_DWORD" /d "1" /f
|
||||
reg add "HKLM\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableWindowsConsumerFeatures" /t "REG_DWORD" /d "1" /f
|
||||
-
|
||||
name: Turn Off Suggested Content in Settings app
|
||||
recommend: standard
|
||||
docs: https://www.tenforums.com/tutorials/100541-turn-off-suggested-content-settings-app-windows-10-a.html
|
||||
code: |-
|
||||
reg add HKCU\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager /v "SubscribedContent-338393Enabled" /d "0" /t REG_DWORD /f
|
||||
reg add HKCU\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager /v "SubscribedContent-353694Enabled" /d "0" /t REG_DWORD /f
|
||||
reg add HKCU\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager /v "SubscribedContent-353696Enabled" /d "0" /t REG_DWORD /f
|
||||
-
|
||||
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-338393Enabled" /d "0" /t REG_DWORD /f
|
||||
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-353694Enabled" /d "0" /t REG_DWORD /f
|
||||
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-353696Enabled" /d "0" /t REG_DWORD /f
|
||||
revertCode: |-
|
||||
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-338393Enabled" /d "1" /t REG_DWORD /f
|
||||
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-353694Enabled" /d "1" /t REG_DWORD /f
|
||||
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-353696Enabled" /d "1" /t REG_DWORD /f
|
||||
-
|
||||
category: Disable biometrics (breaks fingerprinting/facial login)
|
||||
children:
|
||||
-
|
||||
@@ -1788,7 +1815,7 @@ actions:
|
||||
category: Chromium Edge settings
|
||||
children:
|
||||
-
|
||||
name: Disable Edge usage and crash-related data reporting # Obselete since Microsoft Edge version 89
|
||||
name: Disable Edge usage and crash-related data reporting (shows "Your browser is managed") # Obselete since Microsoft Edge version 89
|
||||
recommend: standard
|
||||
docs:
|
||||
- https://admx.help/?Category=EdgeChromium&Policy=Microsoft.Policies.Edge::MetricsReportingEnabled
|
||||
@@ -1796,7 +1823,7 @@ actions:
|
||||
code: reg add "HKLM\SOFTWARE\Policies\Microsoft\Edge" /v "MetricsReportingEnabled" /t REG_DWORD /d 0 /f
|
||||
revertCode: reg delete "HKLM\SOFTWARE\Policies\Microsoft\Edge" /v "MetricsReportingEnabled" /f
|
||||
-
|
||||
name: Disable sending site information # Obselete since Microsoft Edge version 89
|
||||
name: Disable sending site information (shows "Your browser is managed") # Obselete since Microsoft Edge version 89
|
||||
recommend: standard
|
||||
docs:
|
||||
- https://admx.help/?Category=EdgeChromium&Policy=Microsoft.Policies.Edge::SendSiteInfoToImproveServices
|
||||
@@ -1894,7 +1921,7 @@ actions:
|
||||
category: Chrome cleanup
|
||||
children:
|
||||
-
|
||||
name: Do not share scanned software data to Google
|
||||
name: Do not share scanned software data to Google (shows "Your browser is managed")
|
||||
recommend: standard
|
||||
docs:
|
||||
- https://www.chromium.org/administrators/policy-list-3#ChromeCleanupReportingEnabled
|
||||
@@ -1902,7 +1929,7 @@ actions:
|
||||
code: reg add "HKLM\SOFTWARE\Policies\Google\Chrome" /v "ChromeCleanupReportingEnabled" /t REG_DWORD /d 0 /f
|
||||
revertCode: reg delete "HKLM\SOFTWARE\Policies\Google\Chrome" /v "ChromeCleanupReportingEnabled" /f
|
||||
-
|
||||
name: Prevent Chrome from scanning the system for cleanup
|
||||
name: Prevent Chrome from scanning the system for cleanup (shows "Your browser is managed")
|
||||
recommend: standard
|
||||
docs:
|
||||
- https://www.chromium.org/administrators/policy-list-3#ChromeCleanupEnabled
|
||||
@@ -1910,7 +1937,7 @@ actions:
|
||||
code: reg add "HKLM\SOFTWARE\Policies\Google\Chrome" /v "ChromeCleanupEnabled" /t REG_DWORD /d 0 /f
|
||||
revertCode: reg delete "HKLM\SOFTWARE\Policies\Google\Chrome" /v "ChromeCleanupEnabled" /f
|
||||
-
|
||||
name: Disable Chrome metrics reporting
|
||||
name: Disable Chrome metrics reporting (shows "Your browser is managed")
|
||||
recommend: standard
|
||||
docs: https://www.stigviewer.com/stig/google_chrome_v23_windows/2013-01-11/finding/V-35780
|
||||
code: reg add "HKLM\SOFTWARE\Policies\Google\Chrome" /v "MetricsReportingEnabled" /t REG_DWORD /d 0 /f
|
||||
@@ -2009,7 +2036,7 @@ actions:
|
||||
reg add "HKCU\Software\Policies\Microsoft\WindowsMediaPlayer" /v "PreventRadioPresetsRetrieval" /t REG_DWORD /d 1 /f
|
||||
reg add "HKLM\SOFTWARE\Policies\Microsoft\WMDRM" /v "DisableOnline" /t REG_DWORD /d 1 /f
|
||||
-
|
||||
name: Disable dows Media Player Network Sharing Service
|
||||
name: Disable Windows Media Player Network Sharing Service
|
||||
recommend: standard
|
||||
code: sc stop "WMPNetworkSvc" & sc config "WMPNetworkSvc" start=disabled
|
||||
-
|
||||
@@ -2986,7 +3013,7 @@ actions:
|
||||
packageName: Microsoft.Messaging
|
||||
-
|
||||
name: Mixed Reality Portal app
|
||||
docs: https://www.microsoft.com/en-us/p/mixed-reality-portal
|
||||
docs: https://www.microsoft.com/en-us/p/mixed-reality-portal/9ng1h8b3zc7m
|
||||
call:
|
||||
function: UninstallStoreApp
|
||||
parameters:
|
||||
@@ -3004,7 +3031,7 @@ actions:
|
||||
packageName: Microsoft.MicrosoftOfficeHub
|
||||
-
|
||||
name: OneNote app
|
||||
docs: https://www.microsoft.com/en-us/p/onenote-for-windows-10
|
||||
docs: https://www.microsoft.com/en-us/p/onenote-for-windows-10/9wzdncrfhvjl
|
||||
call:
|
||||
function: UninstallStoreApp
|
||||
parameters:
|
||||
@@ -3511,7 +3538,7 @@ actions:
|
||||
parameters:
|
||||
packageName: Microsoft.Windows.CapturePicker
|
||||
-
|
||||
name: Cloud Experience Host app # Allows to connect to corporate domains or Microsoft cloud based services
|
||||
name: Cloud Experience Host app (breaks Microsoft cloud/corporate sign in) # Allows to connect to corporate domains or Microsoft cloud based services
|
||||
recommend: strict
|
||||
call:
|
||||
function: UninstallSystemApp
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getEnumNames, getEnumValues } from '@/application/Common/Enum';
|
||||
import { getEnumNames, getEnumValues, assertInRange } from '@/application/Common/Enum';
|
||||
import { IEntity } from '../infrastructure/Entity/IEntity';
|
||||
import { ICategory } from './ICategory';
|
||||
import { IScript } from './IScript';
|
||||
@@ -21,7 +21,7 @@ export class CategoryCollection implements ICategoryCollection {
|
||||
throw new Error('undefined scripting definition');
|
||||
}
|
||||
this.queryable = makeQueryable(actions);
|
||||
ensureValidOs(os);
|
||||
assertInRange(os, OperatingSystem);
|
||||
ensureValid(this.queryable);
|
||||
ensureNoDuplicates(this.queryable.allCategories);
|
||||
ensureNoDuplicates(this.queryable.allScripts);
|
||||
@@ -54,18 +54,6 @@ export class CategoryCollection implements ICategoryCollection {
|
||||
}
|
||||
}
|
||||
|
||||
function ensureValidOs(os: OperatingSystem): void {
|
||||
if (os === undefined) {
|
||||
throw new Error('undefined os');
|
||||
}
|
||||
if (os === OperatingSystem.Unknown) {
|
||||
throw new Error('unknown os');
|
||||
}
|
||||
if (!(os in OperatingSystem)) {
|
||||
throw new Error(`os "${os}" is out of range`);
|
||||
}
|
||||
}
|
||||
|
||||
function ensureNoDuplicates<TKey>(entities: ReadonlyArray<IEntity<TKey>>) {
|
||||
const totalOccurrencesById = new Map<TKey, number>();
|
||||
for (const entity of entities) {
|
||||
|
||||
@@ -10,5 +10,4 @@ export enum OperatingSystem {
|
||||
Android,
|
||||
iOS,
|
||||
WindowsPhone,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { IProjectInformation } from './IProjectInformation';
|
||||
import { OperatingSystem } from './OperatingSystem';
|
||||
import { assertInRange } from '@/application/Common/Enum';
|
||||
|
||||
export class ProjectInformation implements IProjectInformation {
|
||||
public readonly repositoryWebUrl: string;
|
||||
@@ -42,6 +43,7 @@ function getWebUrl(gitUrl: string) {
|
||||
}
|
||||
|
||||
function getFileName(os: OperatingSystem, version: string): string {
|
||||
assertInRange(os, OperatingSystem);
|
||||
switch (os) {
|
||||
case OperatingSystem.Linux:
|
||||
return `privacy.sexy-${version}.AppImage`;
|
||||
@@ -50,6 +52,6 @@ function getFileName(os: OperatingSystem, version: string): string {
|
||||
case OperatingSystem.Windows:
|
||||
return `privacy.sexy-Setup-${version}.exe`;
|
||||
default:
|
||||
throw new Error(`Unsupported os: ${OperatingSystem[os]}`);
|
||||
throw new RangeError(`Unsupported os: ${OperatingSystem[os]}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,16 +7,7 @@ export class ScriptCode implements IScriptCode {
|
||||
syntax: ILanguageSyntax) {
|
||||
if (!syntax) { throw new Error('undefined syntax'); }
|
||||
validateCode(execute, syntax);
|
||||
if (revert) {
|
||||
try {
|
||||
validateCode(revert, syntax);
|
||||
if (execute === revert) {
|
||||
throw new Error(`Code itself and its reverting code cannot be the same`);
|
||||
}
|
||||
} catch (err) {
|
||||
throw Error(`(revert): ${err.message}`);
|
||||
}
|
||||
}
|
||||
validateRevertCode(revert, execute, syntax);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +16,20 @@ export interface ILanguageSyntax {
|
||||
readonly commonCodeParts: string[];
|
||||
}
|
||||
|
||||
function validateRevertCode(revertCode: string, execute: string, syntax: ILanguageSyntax) {
|
||||
if (!revertCode) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
validateCode(revertCode, syntax);
|
||||
if (execute === revertCode) {
|
||||
throw new Error(`Code itself and its reverting code cannot be the same`);
|
||||
}
|
||||
} catch (err) {
|
||||
throw Error(`(revert): ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function validateCode(code: string, syntax: ILanguageSyntax): void {
|
||||
if (!code || code.length === 0) {
|
||||
throw new Error(`code is empty or undefined`);
|
||||
|
||||
@@ -5,16 +5,20 @@ import fs from 'fs';
|
||||
import child_process from 'child_process';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
|
||||
export async function runCodeAsync(
|
||||
code: string, folderName: string, fileExtension: string,
|
||||
node = getNodeJs(), environment = Environment.CurrentEnvironment): Promise<void> {
|
||||
const dir = node.path.join(node.os.tmpdir(), folderName);
|
||||
await node.fs.promises.mkdir(dir, {recursive: true});
|
||||
const filePath = node.path.join(dir, `run.${fileExtension}`);
|
||||
await node.fs.promises.writeFile(filePath, code);
|
||||
await node.fs.promises.chmod(filePath, '755');
|
||||
const command = getExecuteCommand(filePath, environment);
|
||||
node.child_process.exec(command);
|
||||
export class CodeRunner {
|
||||
constructor(
|
||||
private readonly node = getNodeJs(),
|
||||
private readonly environment = Environment.CurrentEnvironment) {
|
||||
}
|
||||
public async runCodeAsync(code: string, folderName: string, fileExtension: string): Promise<void> {
|
||||
const dir = this.node.path.join(this.node.os.tmpdir(), folderName);
|
||||
await this.node.fs.promises.mkdir(dir, {recursive: true});
|
||||
const filePath = this.node.path.join(dir, `run.${fileExtension}`);
|
||||
await this.node.fs.promises.writeFile(filePath, code);
|
||||
await this.node.fs.promises.chmod(filePath, '755');
|
||||
const command = getExecuteCommand(filePath, this.environment);
|
||||
this.node.child_process.exec(command);
|
||||
}
|
||||
}
|
||||
|
||||
function getExecuteCommand(scriptPath: string, environment: Environment): string {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// This script is running through entire life of the application.
|
||||
// It doesn't have any windows which you can see on screen, opens the main window from here.
|
||||
|
||||
import { app, protocol, BrowserWindow, shell } from 'electron';
|
||||
import { app, protocol, BrowserWindow, shell, screen } from 'electron';
|
||||
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib';
|
||||
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer';
|
||||
import path from 'path';
|
||||
@@ -34,9 +34,10 @@ if (!process.env.IS_TEST) {
|
||||
|
||||
function createWindow() {
|
||||
// Create the browser window.
|
||||
const size = getWindowSize(1350, 955);
|
||||
win = new BrowserWindow({
|
||||
width: 1350,
|
||||
height: 955,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
webPreferences: {
|
||||
contextIsolation: false, // To reach node https://github.com/nklayman/vue-cli-plugin-electron-builder/issues/1285
|
||||
// Use pluginOptions.nodeIntegration, leave this alone
|
||||
@@ -143,3 +144,12 @@ function loadUrlWithNodeWorkaround(window: BrowserWindow, url: string) {
|
||||
window.loadURL(url);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
function getWindowSize(idealWidth: number, idealHeight: number) {
|
||||
let { width, height } = screen.getPrimaryDisplay().workAreaSize;
|
||||
// To ensure not creating a screen bigger than current screen size
|
||||
// Not using "enableLargerThanScreen" as it's macOS only (see https://www.electronjs.org/docs/api/browser-window)
|
||||
width = Math.min(width, idealWidth);
|
||||
height = Math.min(height, idealHeight);
|
||||
return { width, height };
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { IApplicationCode } from '@/application/Context/State/Code/IApplicationCode';
|
||||
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { runCodeAsync } from '@/infrastructure/CodeRunner';
|
||||
import { CodeRunner } from '@/infrastructure/CodeRunner';
|
||||
import { IApplicationContext } from '@/application/Context/IApplicationContext';
|
||||
|
||||
@Component({
|
||||
@@ -116,11 +116,12 @@ function buildFileName(scripting: IScriptingDefinition) {
|
||||
}
|
||||
|
||||
async function executeCodeAsync(context: IApplicationContext) {
|
||||
await runCodeAsync(
|
||||
/*code*/ context.state.code.current,
|
||||
/*appName*/ context.app.info.name,
|
||||
/*fileExtension*/ context.state.collection.scripting.fileExtension,
|
||||
);
|
||||
const runner = new CodeRunner();
|
||||
await runner.runCodeAsync(
|
||||
/*code*/ context.state.code.current,
|
||||
/*appName*/ context.app.info.name,
|
||||
/*fileExtension*/ context.state.collection.scripting.fileExtension,
|
||||
);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { RecommendationLevel } from '@/domain/RecommendationLevel';
|
||||
import { scrambledEqual } from '@/application/Common/Array';
|
||||
import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
|
||||
|
||||
export enum SelectionType {
|
||||
Standard,
|
||||
Strict,
|
||||
All,
|
||||
None,
|
||||
Custom,
|
||||
}
|
||||
|
||||
export class SelectionTypeHandler {
|
||||
constructor(private readonly state: ICategoryCollectionState) {
|
||||
if (!state) { throw new Error('undefined state'); }
|
||||
}
|
||||
public selectType(type: SelectionType) {
|
||||
if (type === SelectionType.Custom) {
|
||||
throw new Error('cannot select custom type');
|
||||
}
|
||||
const selector = selectors.get(type);
|
||||
selector.select(this.state);
|
||||
}
|
||||
public getCurrentSelectionType(): SelectionType {
|
||||
for (const [type, selector] of Array.from(selectors.entries())) {
|
||||
if (selector.isSelected(this.state)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return SelectionType.Custom;
|
||||
}
|
||||
}
|
||||
|
||||
interface ISingleTypeHandler {
|
||||
isSelected: (state: ICategoryCollectionState) => boolean;
|
||||
select: (state: ICategoryCollectionState) => void;
|
||||
}
|
||||
|
||||
const selectors = new Map<SelectionType, ISingleTypeHandler>([
|
||||
[SelectionType.None, {
|
||||
select: (state) =>
|
||||
state.selection.deselectAll(),
|
||||
isSelected: (state) =>
|
||||
state.selection.selectedScripts.length === 0,
|
||||
}],
|
||||
[SelectionType.Standard, getRecommendationLevelSelector(RecommendationLevel.Standard)],
|
||||
[SelectionType.Strict, getRecommendationLevelSelector(RecommendationLevel.Strict)],
|
||||
[SelectionType.All, {
|
||||
select: (state) =>
|
||||
state.selection.selectAll(),
|
||||
isSelected: (state) =>
|
||||
state.selection.selectedScripts.length === state.collection.totalScripts,
|
||||
}],
|
||||
]);
|
||||
|
||||
function getRecommendationLevelSelector(level: RecommendationLevel): ISingleTypeHandler {
|
||||
return {
|
||||
select: (state) => selectOnly(level, state),
|
||||
isSelected: (state) => hasAllSelectedLevelOf(level, state),
|
||||
};
|
||||
}
|
||||
|
||||
function hasAllSelectedLevelOf(level: RecommendationLevel, state: ICategoryCollectionState) {
|
||||
const scripts = state.collection.getScriptsByLevel(level);
|
||||
const selectedScripts = state.selection.selectedScripts;
|
||||
return areAllSelected(scripts, selectedScripts);
|
||||
}
|
||||
|
||||
function selectOnly(level: RecommendationLevel, state: ICategoryCollectionState) {
|
||||
const scripts = state.collection.getScriptsByLevel(level);
|
||||
state.selection.selectOnly(scripts);
|
||||
}
|
||||
|
||||
function areAllSelected(
|
||||
expectedScripts: ReadonlyArray<IScript>,
|
||||
selection: ReadonlyArray<SelectedScript>): boolean {
|
||||
selection = selection.filter((selected) => !selected.revert);
|
||||
if (expectedScripts.length < selection.length) {
|
||||
return false;
|
||||
}
|
||||
const selectedScriptIds = selection.map((script) => script.id);
|
||||
const expectedScriptIds = expectedScripts.map((script) => script.id);
|
||||
return scrambledEqual(selectedScriptIds, expectedScriptIds);
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
<div class="part">
|
||||
<SelectableOption
|
||||
label="None"
|
||||
:enabled="this.currentSelection == SelectionState.None"
|
||||
@click="selectAsync(SelectionState.None)"
|
||||
:enabled="this.currentSelection == SelectionType.None"
|
||||
@click="selectType(SelectionType.None)"
|
||||
v-tooltip=" 'Deselect all selected scripts.<br/>' +
|
||||
'💡 Good start to dive deeper into tweaks and select only what you want.'"
|
||||
/>
|
||||
@@ -15,8 +15,8 @@
|
||||
<div class="part">
|
||||
<SelectableOption
|
||||
label="Standard"
|
||||
:enabled="this.currentSelection == SelectionState.Standard"
|
||||
@click="selectAsync(SelectionState.Standard)"
|
||||
:enabled="this.currentSelection == SelectionType.Standard"
|
||||
@click="selectType(SelectionType.Standard)"
|
||||
v-tooltip=" '🛡️ Balanced for privacy and functionality.<br/>' +
|
||||
'OS and applications will function normally.<br/>' +
|
||||
'💡 Recommended for everyone'"
|
||||
@@ -26,8 +26,8 @@
|
||||
<div class="part">
|
||||
<SelectableOption
|
||||
label="Strict"
|
||||
:enabled="this.currentSelection == SelectionState.Strict"
|
||||
@click="selectAsync(SelectionState.Strict)"
|
||||
:enabled="this.currentSelection == SelectionType.Strict"
|
||||
@click="selectType(SelectionType.Strict)"
|
||||
v-tooltip=" '🚫 Stronger privacy, disables risky functions that may leak your data.<br/>' +
|
||||
'⚠️ Double check to remove sripts where you would trade functionality for privacy<br/>' +
|
||||
'💡 Recommended for daily users that prefers more privacy over non-essential functions'"
|
||||
@@ -37,8 +37,8 @@
|
||||
<div class="part">
|
||||
<SelectableOption
|
||||
label="All"
|
||||
:enabled="this.currentSelection == SelectionState.All"
|
||||
@click="selectAsync(SelectionState.All)"
|
||||
:enabled="this.currentSelection == SelectionType.All"
|
||||
@click="selectType(SelectionType.All)"
|
||||
v-tooltip=" '🔒 Strongest privacy, disabling any functionality that may leak your data.<br/>' +
|
||||
'🛑 Not designed for daily users, it will break important functionalities.<br/>' +
|
||||
'💡 Only recommended for extreme use-cases like crime labs where no leak is acceptable'"
|
||||
@@ -52,112 +52,40 @@
|
||||
import { Component } from 'vue-property-decorator';
|
||||
import { StatefulVue } from '@/presentation/components/Shared/StatefulVue';
|
||||
import SelectableOption from './SelectableOption.vue';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { RecommendationLevel } from '@/domain/RecommendationLevel';
|
||||
import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
|
||||
import { SelectionType, SelectionTypeHandler } from './SelectionTypeHandler';
|
||||
|
||||
enum SelectionState {
|
||||
Standard,
|
||||
Strict,
|
||||
All,
|
||||
None,
|
||||
Custom,
|
||||
}
|
||||
@Component({
|
||||
components: {
|
||||
SelectableOption,
|
||||
},
|
||||
})
|
||||
export default class TheSelector extends StatefulVue {
|
||||
public SelectionState = SelectionState;
|
||||
public currentSelection = SelectionState.None;
|
||||
public SelectionType = SelectionType;
|
||||
public currentSelection = SelectionType.None;
|
||||
private selectionTypeHandler: SelectionTypeHandler;
|
||||
|
||||
public async selectAsync(type: SelectionState): Promise<void> {
|
||||
public async selectType(type: SelectionType) {
|
||||
if (this.currentSelection === type) {
|
||||
return;
|
||||
}
|
||||
const context = await this.getCurrentContextAsync();
|
||||
selectType(context.state, type);
|
||||
this.selectionTypeHandler.selectType(type);
|
||||
}
|
||||
|
||||
protected handleCollectionState(newState: ICategoryCollectionState, oldState: ICategoryCollectionState): void {
|
||||
this.updateSelections(newState);
|
||||
newState.selection.changed.on(() => this.updateSelections(newState));
|
||||
this.selectionTypeHandler = new SelectionTypeHandler(newState);
|
||||
this.updateSelections();
|
||||
newState.selection.changed.on(() => this.updateSelections());
|
||||
if (oldState) {
|
||||
oldState.selection.changed.on(() => this.updateSelections(oldState));
|
||||
oldState.selection.changed.on(() => this.updateSelections());
|
||||
}
|
||||
}
|
||||
|
||||
private updateSelections(state: ICategoryCollectionState) {
|
||||
this.currentSelection = getCurrentSelectionState(state);
|
||||
private updateSelections() {
|
||||
this.currentSelection = this.selectionTypeHandler.getCurrentSelectionType();
|
||||
}
|
||||
}
|
||||
|
||||
interface ITypeSelector {
|
||||
isSelected: (state: ICategoryCollectionState) => boolean;
|
||||
select: (state: ICategoryCollectionState) => void;
|
||||
}
|
||||
|
||||
const selectors = new Map<SelectionState, ITypeSelector>([
|
||||
[SelectionState.None, {
|
||||
select: (state) =>
|
||||
state.selection.deselectAll(),
|
||||
isSelected: (state) =>
|
||||
state.selection.totalSelected === 0,
|
||||
}],
|
||||
[SelectionState.Standard, {
|
||||
select: (state) =>
|
||||
state.selection.selectOnly(
|
||||
state.collection.getScriptsByLevel(RecommendationLevel.Standard)),
|
||||
isSelected: (state) =>
|
||||
hasAllSelectedLevelOf(RecommendationLevel.Standard, state),
|
||||
}],
|
||||
[SelectionState.Strict, {
|
||||
select: (state) =>
|
||||
state.selection.selectOnly(state.collection.getScriptsByLevel(RecommendationLevel.Strict)),
|
||||
isSelected: (state) =>
|
||||
hasAllSelectedLevelOf(RecommendationLevel.Strict, state),
|
||||
}],
|
||||
[SelectionState.All, {
|
||||
select: (state) =>
|
||||
state.selection.selectAll(),
|
||||
isSelected: (state) =>
|
||||
state.selection.totalSelected === state.collection.totalScripts,
|
||||
}],
|
||||
]);
|
||||
|
||||
function selectType(state: ICategoryCollectionState, type: SelectionState) {
|
||||
const selector = selectors.get(type);
|
||||
selector.select(state);
|
||||
}
|
||||
|
||||
function getCurrentSelectionState(state: ICategoryCollectionState): SelectionState {
|
||||
for (const [type, selector] of Array.from(selectors.entries())) {
|
||||
if (selector.isSelected(state)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return SelectionState.Custom;
|
||||
}
|
||||
|
||||
function hasAllSelectedLevelOf(level: RecommendationLevel, state: ICategoryCollectionState) {
|
||||
const scripts = state.collection.getScriptsByLevel(level);
|
||||
const selectedScripts = state.selection.selectedScripts;
|
||||
return areAllSelected(scripts, selectedScripts);
|
||||
}
|
||||
|
||||
function areAllSelected(
|
||||
expectedScripts: ReadonlyArray<IScript>,
|
||||
selection: ReadonlyArray<SelectedScript>): boolean {
|
||||
selection = selection.filter((selected) => !selected.revert);
|
||||
if (expectedScripts.length < selection.length) {
|
||||
return false;
|
||||
}
|
||||
const selectedScriptIds = selection.map((script) => script.id).sort();
|
||||
const expectedScriptIds = expectedScripts.map((script) => script.id).sort();
|
||||
return selectedScriptIds.every((id, index) => id === expectedScriptIds[index]);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -24,7 +24,7 @@ import { ApplicationFactory } from '@/application/ApplicationFactory';
|
||||
@Component
|
||||
export default class TheOsChanger extends StatefulVue {
|
||||
public allOses: Array<{ name: string, os: OperatingSystem }> = [];
|
||||
public currentOs: OperatingSystem = OperatingSystem.Unknown;
|
||||
public currentOs?: OperatingSystem = null;
|
||||
|
||||
public async created() {
|
||||
const app = await ApplicationFactory.Current.getAppAsync();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { ApplicationFactory, ApplicationGetter } from '@/application/ApplicationFactory';
|
||||
import { ApplicationStub } from '../stubs/ApplicationStub';
|
||||
import { ApplicationStub } from '@tests/unit/stubs/ApplicationStub';
|
||||
|
||||
describe('ApplicationFactory', () => {
|
||||
describe('ctor', () => {
|
||||
|
||||
69
tests/unit/application/Common/Array.ComparerTestScenario.ts
Normal file
69
tests/unit/application/Common/Array.ComparerTestScenario.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
interface IComparerTestCase<T> {
|
||||
readonly name: string;
|
||||
readonly first: readonly T[];
|
||||
readonly second: readonly T[];
|
||||
readonly expected: boolean;
|
||||
}
|
||||
|
||||
export class ComparerTestScenario {
|
||||
private readonly testCases: Array<IComparerTestCase<number>> = [];
|
||||
|
||||
public addEmptyArrays(expectedResult: boolean) {
|
||||
return this.addTestCase({
|
||||
name: 'empty array',
|
||||
first: [ ],
|
||||
second: [ ],
|
||||
expected: expectedResult,
|
||||
}, true);
|
||||
}
|
||||
public addSameItemsWithSameOrder(expectedResult: boolean) {
|
||||
return this.addTestCase({
|
||||
name: 'same items with same order',
|
||||
first: [ 1, 2, 3 ],
|
||||
second: [ 1, 2, 3 ],
|
||||
expected: expectedResult,
|
||||
}, true);
|
||||
}
|
||||
public addSameItemsWithDifferentOrder(expectedResult: boolean) {
|
||||
return this.addTestCase({
|
||||
name: 'same items with different order',
|
||||
first: [ 1, 2, 3 ],
|
||||
second: [ 2, 3, 1 ],
|
||||
expected: expectedResult,
|
||||
}, true);
|
||||
}
|
||||
public addDifferentItemsWithSameLength(expectedResult: boolean) {
|
||||
return this.addTestCase({
|
||||
name: 'different items with same length',
|
||||
first: [ 1, 2, 3 ],
|
||||
second: [ 4, 5, 6 ],
|
||||
expected: expectedResult,
|
||||
}, true);
|
||||
}
|
||||
public addDifferentItemsWithDifferentLength(expectedResult: boolean) {
|
||||
return this.addTestCase({
|
||||
name: 'different items with different length',
|
||||
first: [ 1, 2 ],
|
||||
second: [ 3, 4, 5 ],
|
||||
expected: expectedResult,
|
||||
}, true);
|
||||
}
|
||||
public forEachCase(handler: (testCase: IComparerTestCase<number>) => void) {
|
||||
for (const testCase of this.testCases) {
|
||||
handler(testCase);
|
||||
}
|
||||
}
|
||||
|
||||
private addTestCase(testCase: IComparerTestCase<number>, addReversed: boolean) {
|
||||
this.testCases.push(testCase);
|
||||
if (addReversed) {
|
||||
this.testCases.push({
|
||||
name: `${testCase.name} (reversed)`,
|
||||
first: testCase.second,
|
||||
second: testCase.first,
|
||||
expected: testCase.expected,
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
68
tests/unit/application/Common/Array.spec.ts
Normal file
68
tests/unit/application/Common/Array.spec.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { scrambledEqual } from '@/application/Common/Array';
|
||||
import { sequenceEqual } from '@/application/Common/Array';
|
||||
import { ComparerTestScenario } from './Array.ComparerTestScenario';
|
||||
|
||||
describe('Array', () => {
|
||||
describe('scrambledEqual', () => {
|
||||
describe('throws if arguments are undefined', () => {
|
||||
it('first argument is undefined', () => {
|
||||
const expectedError = 'undefined first array';
|
||||
const act = () => scrambledEqual(undefined, []);
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
it('second arguments is undefined', () => {
|
||||
const expectedError = 'undefined second array';
|
||||
const act = () => scrambledEqual([], undefined);
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
});
|
||||
describe('returns as expected', () => {
|
||||
// arrange
|
||||
const scenario = new ComparerTestScenario()
|
||||
.addSameItemsWithSameOrder(true)
|
||||
.addSameItemsWithDifferentOrder(true)
|
||||
.addDifferentItemsWithSameLength(false)
|
||||
.addDifferentItemsWithDifferentLength(false);
|
||||
// act
|
||||
scenario.forEachCase((testCase) => {
|
||||
it(testCase.name, () => {
|
||||
const actual = scrambledEqual(testCase.first, testCase.second);
|
||||
// assert
|
||||
expect(actual).to.equal(testCase.expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('sequenceEqual', () => {
|
||||
describe('throws if arguments are undefined', () => {
|
||||
it('first argument is undefined', () => {
|
||||
const expectedError = 'undefined first array';
|
||||
const act = () => sequenceEqual(undefined, []);
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
it('second arguments is undefined', () => {
|
||||
const expectedError = 'undefined second array';
|
||||
const act = () => sequenceEqual([], undefined);
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
});
|
||||
describe('returns as expected', () => {
|
||||
// arrange
|
||||
const scenario = new ComparerTestScenario()
|
||||
.addSameItemsWithSameOrder(true)
|
||||
.addSameItemsWithDifferentOrder(true)
|
||||
.addDifferentItemsWithSameLength(false)
|
||||
.addDifferentItemsWithDifferentLength(false);
|
||||
// act
|
||||
scenario.forEachCase((testCase) => {
|
||||
it(testCase.name, () => {
|
||||
const actual = scrambledEqual(testCase.first, testCase.second);
|
||||
// assert
|
||||
expect(actual).to.equal(testCase.expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { getEnumNames, getEnumValues, createEnumParser } from '@/application/Common/Enum';
|
||||
import { getEnumNames, getEnumValues, createEnumParser, assertInRange } from '@/application/Common/Enum';
|
||||
import { EnumRangeTestRunner } from './EnumRangeTestRunner';
|
||||
import { scrambledEqual } from '@/application/Common/Array';
|
||||
|
||||
describe('Enum', () => {
|
||||
describe('createEnumParser', () => {
|
||||
@@ -78,7 +80,7 @@ describe('Enum', () => {
|
||||
// act
|
||||
const actual = getEnumNames(TestEnum);
|
||||
// assert
|
||||
expect(expected.sort()).to.deep.equal(actual.sort());
|
||||
expect(scrambledEqual(expected, actual));
|
||||
});
|
||||
});
|
||||
describe('getEnumValues', () => {
|
||||
@@ -89,7 +91,19 @@ describe('Enum', () => {
|
||||
// act
|
||||
const actual = getEnumValues(TestEnum);
|
||||
// assert
|
||||
expect(expected.sort()).to.deep.equal(actual.sort());
|
||||
expect(scrambledEqual(expected, actual));
|
||||
});
|
||||
});
|
||||
describe('assertInRange', () => {
|
||||
// arrange
|
||||
enum TestEnum { Red, Green, Blue }
|
||||
const validValue = TestEnum.Red;
|
||||
// act
|
||||
const act = (value: TestEnum) => assertInRange(value, TestEnum);
|
||||
// assert
|
||||
new EnumRangeTestRunner(act)
|
||||
.testOutOfRangeThrows()
|
||||
.testUndefinedValueThrows()
|
||||
.testValidValueDoesNotThrow(validValue);
|
||||
});
|
||||
});
|
||||
|
||||
54
tests/unit/application/Common/EnumRangeTestRunner.ts
Normal file
54
tests/unit/application/Common/EnumRangeTestRunner.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { EnumType } from '@/application/Common/Enum';
|
||||
|
||||
export class EnumRangeTestRunner<TEnumValue extends EnumType> {
|
||||
constructor(private readonly runner: (value: TEnumValue) => any) {
|
||||
}
|
||||
public testOutOfRangeThrows() {
|
||||
it('throws when value is out of range', () => {
|
||||
// arrange
|
||||
const value = Number.MAX_SAFE_INTEGER as TEnumValue;
|
||||
const expectedError = `enum value "${value}" is out of range`;
|
||||
// act
|
||||
const act = () => this.runner(value);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
public testUndefinedValueThrows() {
|
||||
it('throws when value is undefined', () => {
|
||||
// arrange
|
||||
const value = undefined;
|
||||
const expectedError = 'undefined enum value';
|
||||
// act
|
||||
const act = () => this.runner(value);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
public testInvalidValueThrows(invalidValue: TEnumValue, expectedError: string) {
|
||||
it(`throws ${expectedError}`, () => {
|
||||
// arrange
|
||||
const value = invalidValue;
|
||||
// act
|
||||
const act = () => this.runner(value);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
public testValidValueDoesNotThrow(validValue: TEnumValue) {
|
||||
it('throws when value is undefined', () => {
|
||||
// arrange
|
||||
const value = validValue;
|
||||
// act
|
||||
const act = () => this.runner(value);
|
||||
// assert
|
||||
expect(act).to.not.throw();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { ScriptingLanguageFactory } from '@/application/Common/ScriptingLanguage/ScriptingLanguageFactory';
|
||||
import { ScriptingLanguageFactoryTestRunner } from './ScriptingLanguageFactoryTestRunner';
|
||||
import { EnumRangeTestRunner } from '@tests/unit/application/Common/EnumRangeTestRunner';
|
||||
|
||||
class ScriptingLanguageConcrete extends ScriptingLanguageFactory<number> {
|
||||
public registerGetter(language: ScriptingLanguage, getter: () => number) {
|
||||
super.registerGetter(language, getter);
|
||||
}
|
||||
}
|
||||
|
||||
describe('ScriptingLanguageFactory', () => {
|
||||
describe('registerGetter', () => {
|
||||
describe('validates language', () => {
|
||||
// arrange
|
||||
const validValue = ScriptingLanguage.batchfile;
|
||||
const getter = () => undefined;
|
||||
const sut = new ScriptingLanguageConcrete();
|
||||
// act
|
||||
const act = (language: ScriptingLanguage) => sut.registerGetter(language, getter);
|
||||
// assert
|
||||
new EnumRangeTestRunner(act)
|
||||
.testOutOfRangeThrows()
|
||||
.testUndefinedValueThrows()
|
||||
.testValidValueDoesNotThrow(validValue);
|
||||
});
|
||||
it('throw when getter is undefined', () => {
|
||||
// arrange
|
||||
const expectedError = `undefined getter`;
|
||||
const language = ScriptingLanguage.batchfile;
|
||||
const getter = undefined;
|
||||
const sut = new ScriptingLanguageConcrete();
|
||||
// act
|
||||
const act = () => sut.registerGetter(language, getter);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
it('throw when language is already registered', () => {
|
||||
// arrange
|
||||
const language = ScriptingLanguage.batchfile;
|
||||
const expectedError = `${ScriptingLanguage[language]} is already registered`;
|
||||
const getter = () => undefined;
|
||||
const sut = new ScriptingLanguageConcrete();
|
||||
// act
|
||||
sut.registerGetter(language, getter);
|
||||
const reRegister = () => sut.registerGetter(language, getter);
|
||||
// assert
|
||||
expect(reRegister).to.throw(expectedError);
|
||||
});
|
||||
});
|
||||
describe('create', () => {
|
||||
const sut = new ScriptingLanguageConcrete();
|
||||
sut.registerGetter(ScriptingLanguage.batchfile, () => undefined);
|
||||
const runner = new ScriptingLanguageFactoryTestRunner();
|
||||
runner.testCreateMethod(sut);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { IScriptingLanguageFactory } from '@/application/Common/ScriptingLanguage/IScriptingLanguageFactory';
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { EnumRangeTestRunner } from '@tests/unit/application/Common/EnumRangeTestRunner';
|
||||
|
||||
export class ScriptingLanguageFactoryTestRunner<T> {
|
||||
private expectedTypes = new Map<ScriptingLanguage, T>();
|
||||
public expect(language: ScriptingLanguage, resultType: T) {
|
||||
this.expectedTypes.set(language, resultType);
|
||||
return this;
|
||||
}
|
||||
public testCreateMethod(sut: IScriptingLanguageFactory<T>) {
|
||||
if (!sut) { throw new Error('undefined sut'); }
|
||||
testLanguageValidation(sut);
|
||||
testExpectedInstanceTypes(sut, this.expectedTypes);
|
||||
}
|
||||
}
|
||||
|
||||
function testExpectedInstanceTypes<T>(
|
||||
sut: IScriptingLanguageFactory<T>,
|
||||
expectedTypes: Map<ScriptingLanguage, T>) {
|
||||
describe('create returns expected instances', () => {
|
||||
// arrange
|
||||
for (const language of Array.from(expectedTypes.keys())) {
|
||||
it(ScriptingLanguage[language], () => {
|
||||
// act
|
||||
const expected = expectedTypes.get(language);
|
||||
const result = sut.create(language);
|
||||
// assert
|
||||
expect(result).to.be.instanceOf(expected, `Actual was: ${result.constructor.name}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function testLanguageValidation<T>(sut: IScriptingLanguageFactory<T>) {
|
||||
describe('validates language', () => {
|
||||
// arrange
|
||||
const validValue = ScriptingLanguage.batchfile;
|
||||
// act
|
||||
const act = (value: ScriptingLanguage) => sut.create(value);
|
||||
// assert
|
||||
new EnumRangeTestRunner(act)
|
||||
.testOutOfRangeThrows()
|
||||
.testUndefinedValueThrows()
|
||||
.testValidValueDoesNotThrow(validValue);
|
||||
});
|
||||
}
|
||||
@@ -5,8 +5,9 @@ import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
|
||||
import { IApplicationContext, IApplicationContextChangedEvent } from '@/application/Context/IApplicationContext';
|
||||
import { IApplication } from '@/domain/IApplication';
|
||||
import { ApplicationStub } from '../../stubs/ApplicationStub';
|
||||
import { CategoryCollectionStub } from '../../stubs/CategoryCollectionStub';
|
||||
import { ApplicationStub } from '@tests/unit/stubs/ApplicationStub';
|
||||
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
|
||||
import { EnumRangeTestRunner } from '@tests/unit/application/Common/EnumRangeTestRunner';
|
||||
|
||||
describe('ApplicationContext', () => {
|
||||
describe('changeContext', () => {
|
||||
@@ -180,40 +181,15 @@ describe('ApplicationContext', () => {
|
||||
expect(actual).to.deep.equal(expected);
|
||||
});
|
||||
describe('throws when OS is invalid', () => {
|
||||
// arrange
|
||||
const testCases = [
|
||||
{
|
||||
name: 'out of range',
|
||||
expectedError: 'os "9999" is out of range',
|
||||
os: 9999,
|
||||
},
|
||||
{
|
||||
name: 'undefined',
|
||||
expectedError: 'undefined os',
|
||||
os: undefined,
|
||||
},
|
||||
{
|
||||
name: 'unknown',
|
||||
expectedError: 'unknown os',
|
||||
os: OperatingSystem.Unknown,
|
||||
},
|
||||
{
|
||||
name: 'does not exist in application',
|
||||
expectedError: 'os "Android" is not defined in application',
|
||||
os: OperatingSystem.Android,
|
||||
},
|
||||
];
|
||||
// act
|
||||
for (const testCase of testCases) {
|
||||
it(testCase.name, () => {
|
||||
const act = () =>
|
||||
new ObservableApplicationContextFactory()
|
||||
.withInitialOs(testCase.os)
|
||||
.construct();
|
||||
// assert
|
||||
expect(act).to.throw(testCase.expectedError);
|
||||
});
|
||||
}
|
||||
const act = (os: OperatingSystem) => new ObservableApplicationContextFactory()
|
||||
.withInitialOs(os)
|
||||
.construct();
|
||||
// assert
|
||||
new EnumRangeTestRunner(act)
|
||||
.testOutOfRangeThrows()
|
||||
.testUndefinedValueThrows()
|
||||
.testInvalidValueThrows(OperatingSystem.Android, 'os "Android" is not defined in application');
|
||||
});
|
||||
});
|
||||
describe('app', () => {
|
||||
|
||||
@@ -2,12 +2,12 @@ import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import { CategoryCollectionStub } from '../../stubs/CategoryCollectionStub';
|
||||
import { EnvironmentStub } from '../../stubs/EnvironmentStub';
|
||||
import { ApplicationStub } from '../../stubs/ApplicationStub';
|
||||
import { buildContextAsync } from '@/application/Context/ApplicationContextFactory';
|
||||
import { IApplicationFactory } from '@/application/IApplicationFactory';
|
||||
import { IApplication } from '@/domain/IApplication';
|
||||
import { EnvironmentStub } from '@tests/unit/stubs/EnvironmentStub';
|
||||
import { ApplicationStub } from '@tests/unit/stubs/ApplicationStub';
|
||||
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
|
||||
|
||||
describe('ApplicationContextFactory', () => {
|
||||
describe('buildContextAsync', () => {
|
||||
|
||||
@@ -5,9 +5,9 @@ import { ApplicationCode } from '@/application/Context/State/Code/ApplicationCod
|
||||
import { CategoryCollectionState } from '@/application/Context/State/CategoryCollectionState';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { ScriptStub } from '../../../stubs/ScriptStub';
|
||||
import { CategoryStub } from '../../../stubs/CategoryStub';
|
||||
import { CategoryCollectionStub } from '../../../stubs/CategoryCollectionStub';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
|
||||
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
|
||||
|
||||
describe('CategoryCollectionState', () => {
|
||||
describe('code', () => {
|
||||
@@ -55,7 +55,7 @@ describe('CategoryCollectionState', () => {
|
||||
const collection = new CategoryCollectionStub();
|
||||
const sut = new CategoryCollectionState(collection);
|
||||
// act
|
||||
const actual = sut.selection.totalSelected;
|
||||
const actual = sut.selection.selectedScripts.length;
|
||||
// assert
|
||||
expect(actual).to.equal(0);
|
||||
});
|
||||
@@ -68,7 +68,7 @@ describe('CategoryCollectionState', () => {
|
||||
// act
|
||||
sut.selection.selectAll();
|
||||
// assert
|
||||
expect(sut.selection.totalSelected).to.equal(1);
|
||||
expect(sut.selection.selectedScripts.length).to.equal(1);
|
||||
expect(sut.selection.isSelected(expectedScript.id)).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,10 +9,10 @@ import { CodePosition } from '@/application/Context/State/Code/Position/CodePosi
|
||||
import { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
import { IUserScript } from '@/application/Context/State/Code/Generation/IUserScript';
|
||||
import { ScriptingDefinitionStub } from '../../../../stubs/ScriptingDefinitionStub';
|
||||
import { CategoryStub } from '../../../../stubs/CategoryStub';
|
||||
import { ScriptStub } from '../../../../stubs/ScriptStub';
|
||||
import { CategoryCollectionStub } from '../../../../stubs/CategoryCollectionStub';
|
||||
import { ScriptingDefinitionStub } from '@tests/unit/stubs/ScriptingDefinitionStub';
|
||||
import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
|
||||
|
||||
describe('ApplicationCode', () => {
|
||||
describe('ctor', () => {
|
||||
|
||||
@@ -4,8 +4,8 @@ import { CodeChangedEvent } from '@/application/Context/State/Code/Event/CodeCha
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||
import { CodePosition } from '@/application/Context/State/Code/Position/CodePosition';
|
||||
import { SelectedScriptStub } from '../../../../../stubs/SelectedScriptStub';
|
||||
import { ScriptStub } from '../../../../../stubs/ScriptStub';
|
||||
import { SelectedScriptStub } from '@tests/unit/stubs/SelectedScriptStub';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
|
||||
describe('CodeChangedEvent', () => {
|
||||
describe('ctor', () => {
|
||||
|
||||
@@ -1,36 +1,13 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { ShellBuilder } from '@/application/Context/State/Code/Generation/Languages/ShellBuilder';
|
||||
import { BatchBuilder } from '@/application/Context/State/Code/Generation/Languages/BatchBuilder';
|
||||
import { CodeBuilderFactory } from '@/application/Context/State/Code/Generation/CodeBuilderFactory';
|
||||
import { ScriptingLanguageFactoryTestRunner } from '@tests/unit/application/Common/ScriptingLanguage/ScriptingLanguageFactoryTestRunner';
|
||||
|
||||
describe('CodeBuilderFactory', () => {
|
||||
describe('create', () => {
|
||||
describe('creates expected type', () => {
|
||||
// arrange
|
||||
const testCases: Array< { language: ScriptingLanguage, expected: any} > = [
|
||||
{ language: ScriptingLanguage.shellscript, expected: ShellBuilder},
|
||||
{ language: ScriptingLanguage.batchfile, expected: BatchBuilder},
|
||||
];
|
||||
for (const testCase of testCases) {
|
||||
it(ScriptingLanguage[testCase.language], () => {
|
||||
// act
|
||||
const sut = new CodeBuilderFactory();
|
||||
const result = sut.create(testCase.language);
|
||||
// assert
|
||||
expect(result).to.be.instanceOf(testCase.expected,
|
||||
`Actual was: ${result.constructor.name}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
it('throws on unknown scripting language', () => {
|
||||
// arrange
|
||||
const sut = new CodeBuilderFactory();
|
||||
// act
|
||||
const act = () => sut.create(3131313131);
|
||||
// assert
|
||||
expect(act).to.throw(`unknown language: "${ScriptingLanguage[3131313131]}"`);
|
||||
});
|
||||
});
|
||||
const sut = new CodeBuilderFactory();
|
||||
const runner = new ScriptingLanguageFactoryTestRunner()
|
||||
.expect(ScriptingLanguage.shellscript, ShellBuilder)
|
||||
.expect(ScriptingLanguage.batchfile, BatchBuilder);
|
||||
runner.testCreateMethod(sut);
|
||||
});
|
||||
|
||||
@@ -4,8 +4,8 @@ import { UserScriptGenerator } from '@/application/Context/State/Code/Generation
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { ICodeBuilderFactory } from '@/application/Context/State/Code/Generation/ICodeBuilderFactory';
|
||||
import { ICodeBuilder } from '@/application/Context/State/Code/Generation/ICodeBuilder';
|
||||
import { ScriptStub } from '../../../../../stubs/ScriptStub';
|
||||
import { ScriptingDefinitionStub } from '../../../../../stubs/ScriptingDefinitionStub';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
import { ScriptingDefinitionStub } from '@tests/unit/stubs/ScriptingDefinitionStub';
|
||||
|
||||
describe('UserScriptGenerator', () => {
|
||||
describe('scriptingDefinition', () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CodePosition } from '@/application/Context/State/Code/Position/CodePosition';
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { CodePosition } from '@/application/Context/State/Code/Position/CodePosition';
|
||||
|
||||
describe('CodePosition', () => {
|
||||
describe('ctor', () => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { CategoryStub } from '../../../../stubs/CategoryStub';
|
||||
import { ScriptStub } from '../../../../stubs/ScriptStub';
|
||||
import { FilterResult } from '@/application/Context/State/Filter/FilterResult';
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { FilterResult } from '@/application/Context/State/Filter/FilterResult';
|
||||
import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
|
||||
describe('FilterResult', () => {
|
||||
describe('hasAnyMatches', () => {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { IFilterResult } from '@/application/Context/State/Filter/IFilterResult';
|
||||
import { UserFilter } from '@/application/Context/State/Filter/UserFilter';
|
||||
import { CategoryStub } from '../../../../stubs/CategoryStub';
|
||||
import { ScriptStub } from '../../../../stubs/ScriptStub';
|
||||
import { CategoryCollectionStub } from '../../../../stubs/CategoryCollectionStub';
|
||||
import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
|
||||
|
||||
describe('UserFilter', () => {
|
||||
describe('removeFilter', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ScriptStub } from '../../../../stubs/ScriptStub';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
|
||||
describe('SelectedScript', () => {
|
||||
it('id is same as script id', () => {
|
||||
|
||||
@@ -3,10 +3,10 @@ import { expect } from 'chai';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
|
||||
import { CategoryStub } from '../../../../stubs/CategoryStub';
|
||||
import { CategoryCollectionStub } from '../../../../stubs/CategoryCollectionStub';
|
||||
import { SelectedScriptStub } from '../../../../stubs/SelectedScriptStub';
|
||||
import { ScriptStub } from '../../../../stubs/ScriptStub';
|
||||
import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
|
||||
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
|
||||
import { SelectedScriptStub } from '@tests/unit/stubs/SelectedScriptStub';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
|
||||
describe('UserSelection', () => {
|
||||
describe('ctor', () => {
|
||||
@@ -152,7 +152,6 @@ describe('UserSelection', () => {
|
||||
// act
|
||||
sut.removeAllInCategory(categoryId);
|
||||
// assert
|
||||
expect(sut.totalSelected).to.equal(0);
|
||||
expect(sut.selectedScripts.length).to.equal(0);
|
||||
});
|
||||
it('removes existing some exists', () => {
|
||||
@@ -167,7 +166,6 @@ describe('UserSelection', () => {
|
||||
// act
|
||||
sut.removeAllInCategory(categoryId);
|
||||
// assert
|
||||
expect(sut.totalSelected).to.equal(0);
|
||||
expect(sut.selectedScripts.length).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { BrowserOsDetector } from '@/application/Environment/BrowserOs/BrowserOsDetector';
|
||||
import { BrowserOsTestCases } from './BrowserOsTestCases';
|
||||
|
||||
describe('BrowserOsDetector', () => {
|
||||
it('unkown when user agent is undefined', () => {
|
||||
it('returns undefined when user agent is undefined', () => {
|
||||
// arrange
|
||||
const expected = undefined;
|
||||
const sut = new BrowserOsDetector();
|
||||
// act
|
||||
const actual = sut.detect(undefined);
|
||||
// assert
|
||||
expect(actual).to.equal(OperatingSystem.Unknown);
|
||||
expect(actual).to.equal(expected);
|
||||
});
|
||||
it('detects as expected', () => {
|
||||
for (const testCase of BrowserOsTestCases) {
|
||||
|
||||
@@ -9,7 +9,7 @@ interface IDesktopTestCase {
|
||||
export const DesktopOsTestCases: ReadonlyArray<IDesktopTestCase> = [
|
||||
{
|
||||
processPlatform: 'aix',
|
||||
expectedOs: OperatingSystem.Unknown,
|
||||
expectedOs: undefined,
|
||||
},
|
||||
{
|
||||
processPlatform: 'darwin',
|
||||
@@ -17,7 +17,7 @@ export const DesktopOsTestCases: ReadonlyArray<IDesktopTestCase> = [
|
||||
},
|
||||
{
|
||||
processPlatform: 'freebsd',
|
||||
expectedOs: OperatingSystem.Unknown,
|
||||
expectedOs: undefined,
|
||||
},
|
||||
{
|
||||
processPlatform: 'linux',
|
||||
@@ -25,11 +25,11 @@ export const DesktopOsTestCases: ReadonlyArray<IDesktopTestCase> = [
|
||||
},
|
||||
{
|
||||
processPlatform: 'openbsd',
|
||||
expectedOs: OperatingSystem.Unknown,
|
||||
expectedOs: undefined,
|
||||
},
|
||||
{
|
||||
processPlatform: 'sunos',
|
||||
expectedOs: OperatingSystem.Unknown,
|
||||
expectedOs: undefined,
|
||||
},
|
||||
{
|
||||
processPlatform: 'win32',
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { IBrowserOsDetector } from '@/application/Environment/BrowserOs/IBrowserOsDetector';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { DesktopOsTestCases } from './DesktopOsTestCases';
|
||||
import { Environment } from '@/application/Environment/Environment';
|
||||
import { expect } from 'chai';
|
||||
|
||||
interface EnvironmentVariables {
|
||||
window?: any;
|
||||
|
||||
@@ -10,9 +10,9 @@ import { ProjectInformation } from '@/domain/ProjectInformation';
|
||||
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { getEnumValues } from '@/application/Common/Enum';
|
||||
import { CategoryCollectionStub } from '../../stubs/CategoryCollectionStub';
|
||||
import { getProcessEnvironmentStub } from '../../stubs/ProcessEnvironmentStub';
|
||||
import { CollectionDataStub } from '../../stubs/CollectionDataStub';
|
||||
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
|
||||
import { getProcessEnvironmentStub } from '@tests/unit/stubs/ProcessEnvironmentStub';
|
||||
import { CollectionDataStub } from '@tests/unit/stubs/CollectionDataStub';
|
||||
|
||||
describe('ApplicationParser', () => {
|
||||
describe('parseApplication', () => {
|
||||
|
||||
@@ -7,13 +7,13 @@ import { parseProjectInformation } from '@/application/Parser/ProjectInformation
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { RecommendationLevel } from '@/domain/RecommendationLevel';
|
||||
import { ScriptingDefinitionParser } from '@/application/Parser/ScriptingDefinition/ScriptingDefinitionParser';
|
||||
import { EnumParserStub } from '../../stubs/EnumParserStub';
|
||||
import { ProjectInformationStub } from '../../stubs/ProjectInformationStub';
|
||||
import { getCategoryStub, CollectionDataStub } from '../../stubs/CollectionDataStub';
|
||||
import { CategoryCollectionParseContextStub } from '../../stubs/CategoryCollectionParseContextStub';
|
||||
import { CategoryDataStub } from '../../stubs/CategoryDataStub';
|
||||
import { ScriptDataStub } from '../../stubs/ScriptDataStub';
|
||||
import { FunctionDataStub } from '../../stubs/FunctionDataStub';
|
||||
import { EnumParserStub } from '@tests/unit/stubs/EnumParserStub';
|
||||
import { ProjectInformationStub } from '@tests/unit/stubs/ProjectInformationStub';
|
||||
import { getCategoryStub, CollectionDataStub } from '@tests/unit/stubs/CollectionDataStub';
|
||||
import { CategoryCollectionParseContextStub } from '@tests/unit/stubs/CategoryCollectionParseContextStub';
|
||||
import { CategoryDataStub } from '@tests/unit/stubs/CategoryDataStub';
|
||||
import { ScriptDataStub } from '@tests/unit/stubs/ScriptDataStub';
|
||||
import { FunctionDataStub } from '@tests/unit/stubs/FunctionDataStub';
|
||||
|
||||
describe('CategoryCollectionParser', () => {
|
||||
describe('parseCategoryCollection', () => {
|
||||
|
||||
@@ -3,11 +3,11 @@ import { expect } from 'chai';
|
||||
import { parseCategory } from '@/application/Parser/CategoryParser';
|
||||
import { parseScript } from '@/application/Parser/Script/ScriptParser';
|
||||
import { parseDocUrls } from '@/application/Parser/DocumentationParser';
|
||||
import { ScriptCompilerStub } from '../../stubs/ScriptCompilerStub';
|
||||
import { ScriptDataStub } from '../../stubs/ScriptDataStub';
|
||||
import { CategoryCollectionParseContextStub } from '../../stubs/CategoryCollectionParseContextStub';
|
||||
import { LanguageSyntaxStub } from '../../stubs/LanguageSyntaxStub';
|
||||
import { CategoryDataStub } from '../../stubs/CategoryDataStub';
|
||||
import { ScriptCompilerStub } from '@tests/unit/stubs/ScriptCompilerStub';
|
||||
import { ScriptDataStub } from '@tests/unit/stubs/ScriptDataStub';
|
||||
import { CategoryCollectionParseContextStub } from '@tests/unit/stubs/CategoryCollectionParseContextStub';
|
||||
import { LanguageSyntaxStub } from '@tests/unit/stubs/LanguageSyntaxStub';
|
||||
import { CategoryDataStub } from '@tests/unit/stubs/CategoryDataStub';
|
||||
|
||||
describe('CategoryParser', () => {
|
||||
describe('parseCategory', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { parseProjectInformation } from '@/application/Parser/ProjectInformationParser';
|
||||
import { getProcessEnvironmentStub } from '../../stubs/ProcessEnvironmentStub';
|
||||
import { getProcessEnvironmentStub } from '@tests/unit/stubs/ProcessEnvironmentStub';
|
||||
|
||||
describe('ProjectInformationParser', () => {
|
||||
describe('parseProjectInformation', () => {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { FunctionData } from 'js-yaml-loader!@/*';
|
||||
import { ISyntaxFactory } from '@/application/Parser/Script/Syntax/ISyntaxFactory';
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { LanguageSyntaxStub } from '../../../stubs/LanguageSyntaxStub';
|
||||
import { CategoryCollectionParseContext } from '@/application/Parser/Script/CategoryCollectionParseContext';
|
||||
import { ScriptingDefinitionStub } from '../../../stubs/ScriptingDefinitionStub';
|
||||
import { FunctionDataStub } from '../../../stubs/FunctionDataStub';
|
||||
import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||
import { ScriptCompiler } from '@/application/Parser/Script/Compiler/ScriptCompiler';
|
||||
import { FunctionData } from 'js-yaml-loader!*';
|
||||
import { LanguageSyntaxStub } from '@tests/unit/stubs/LanguageSyntaxStub';
|
||||
import { ScriptingDefinitionStub } from '@tests/unit/stubs/ScriptingDefinitionStub';
|
||||
import { FunctionDataStub } from '@tests/unit/stubs/FunctionDataStub';
|
||||
|
||||
describe('CategoryCollectionParseContext', () => {
|
||||
describe('ctor', () => {
|
||||
|
||||
@@ -2,8 +2,8 @@ import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { ExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/ExpressionsCompiler';
|
||||
import { IExpressionParser } from '@/application/Parser/Script/Compiler/Expressions/Parser/IExpressionParser';
|
||||
import { ExpressionStub } from '../../../../../stubs/ExpressionStub';
|
||||
import { ExpressionParserStub } from '../../../../../stubs/ExpressionParserStub';
|
||||
import { ExpressionStub } from '@tests/unit/stubs/ExpressionStub';
|
||||
import { ExpressionParserStub } from '@tests/unit/stubs/ExpressionParserStub';
|
||||
|
||||
describe('ExpressionsCompiler', () => {
|
||||
describe('compileExpressions', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { expect } from 'chai';
|
||||
import { IExpression } from '@/application/Parser/Script/Compiler/Expressions/Expression/IExpression';
|
||||
import { IExpressionParser } from '@/application/Parser/Script/Compiler/Expressions/Parser/IExpressionParser';
|
||||
import { CompositeExpressionParser } from '@/application/Parser/Script/Compiler/Expressions/Parser/CompositeExpressionParser';
|
||||
import { ExpressionStub } from '../../../../../../stubs/ExpressionStub';
|
||||
import { ExpressionStub } from '@tests/unit/stubs/ExpressionStub';
|
||||
|
||||
describe('CompositeExpressionParser', () => {
|
||||
describe('ctor', () => {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { ISharedFunction } from '@/application/Parser/Script/Compiler/Function/ISharedFunction';
|
||||
import { FunctionData } from 'js-yaml-loader!*';
|
||||
import { FunctionData } from 'js-yaml-loader!@/*';
|
||||
import { IFunctionCallCompiler } from '@/application/Parser/Script/Compiler/FunctionCall/IFunctionCallCompiler';
|
||||
import { FunctionCompiler } from '@/application/Parser/Script/Compiler/Function/FunctionCompiler';
|
||||
import { FunctionCallCompilerStub } from '../../../../../stubs/FunctionCallCompilerStub';
|
||||
import { FunctionDataStub } from '../../../../../stubs/FunctionDataStub';
|
||||
import { FunctionCallCompilerStub } from '@tests/unit/stubs/FunctionCallCompilerStub';
|
||||
import { FunctionDataStub } from '@tests/unit/stubs/FunctionDataStub';
|
||||
|
||||
describe('FunctionsCompiler', () => {
|
||||
describe('compileFunctions', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { SharedFunctionCollection } from '@/application/Parser/Script/Compiler/Function/SharedFunctionCollection';
|
||||
import { SharedFunctionStub } from '../../../../../stubs/SharedFunctionStub';
|
||||
import { SharedFunctionStub } from '@tests/unit/stubs/SharedFunctionStub';
|
||||
|
||||
describe('SharedFunctionCollection', () => {
|
||||
describe('addFunction', () => {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { FunctionCallData, FunctionCallParametersData } from 'js-yaml-loader!*';
|
||||
import { FunctionCallData, FunctionCallParametersData } from 'js-yaml-loader!@/*';
|
||||
import { FunctionCallCompiler } from '@/application/Parser/Script/Compiler/FunctionCall/FunctionCallCompiler';
|
||||
import { ISharedFunctionCollection } from '@/application/Parser/Script/Compiler/Function/ISharedFunctionCollection';
|
||||
import { IExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/IExpressionsCompiler';
|
||||
import { ExpressionsCompilerStub } from '../../../../../stubs/ExpressionsCompilerStub';
|
||||
import { SharedFunctionCollectionStub } from '../../../../../stubs/SharedFunctionCollectionStub';
|
||||
import { SharedFunctionStub } from '../../../../../stubs/SharedFunctionStub';
|
||||
import { ExpressionsCompilerStub } from '@tests/unit/stubs/ExpressionsCompilerStub';
|
||||
import { SharedFunctionCollectionStub } from '@tests/unit/stubs/SharedFunctionCollectionStub';
|
||||
import { SharedFunctionStub } from '@tests/unit/stubs/SharedFunctionStub';
|
||||
|
||||
describe('FunctionCallCompiler', () => {
|
||||
describe('compileCall', () => {
|
||||
|
||||
@@ -6,12 +6,12 @@ import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||
import { IFunctionCompiler } from '@/application/Parser/Script/Compiler/Function/IFunctionCompiler';
|
||||
import { IFunctionCallCompiler } from '@/application/Parser/Script/Compiler/FunctionCall/IFunctionCallCompiler';
|
||||
import { ICompiledCode } from '@/application/Parser/Script/Compiler/FunctionCall/ICompiledCode';
|
||||
import { LanguageSyntaxStub } from '../../../../stubs/LanguageSyntaxStub';
|
||||
import { ScriptDataStub } from '../../../../stubs/ScriptDataStub';
|
||||
import { FunctionDataStub } from '../../../../stubs/FunctionDataStub';
|
||||
import { FunctionCallCompilerStub } from '../../../../stubs/FunctionCallCompilerStub';
|
||||
import { FunctionCompilerStub } from '../../../../stubs/FunctionCompilerStub';
|
||||
import { SharedFunctionCollectionStub } from '../../../../stubs/SharedFunctionCollectionStub';
|
||||
import { LanguageSyntaxStub } from '@tests/unit/stubs/LanguageSyntaxStub';
|
||||
import { ScriptDataStub } from '@tests/unit/stubs/ScriptDataStub';
|
||||
import { FunctionDataStub } from '@tests/unit/stubs/FunctionDataStub';
|
||||
import { FunctionCallCompilerStub } from '@tests/unit/stubs/FunctionCallCompilerStub';
|
||||
import { FunctionCompilerStub } from '@tests/unit/stubs/FunctionCompilerStub';
|
||||
import { SharedFunctionCollectionStub } from '@tests/unit/stubs/SharedFunctionCollectionStub';
|
||||
|
||||
describe('ScriptCompiler', () => {
|
||||
describe('ctor', () => {
|
||||
|
||||
@@ -4,12 +4,12 @@ import { parseScript } from '@/application/Parser/Script/ScriptParser';
|
||||
import { parseDocUrls } from '@/application/Parser/DocumentationParser';
|
||||
import { RecommendationLevel } from '@/domain/RecommendationLevel';
|
||||
import { ICategoryCollectionParseContext } from '@/application/Parser/Script/ICategoryCollectionParseContext';
|
||||
import { ScriptCompilerStub } from '../../../stubs/ScriptCompilerStub';
|
||||
import { ScriptDataStub } from '../../../stubs/ScriptDataStub';
|
||||
import { EnumParserStub } from '../../../stubs/EnumParserStub';
|
||||
import { ScriptCodeStub } from '../../../stubs/ScriptCodeStub';
|
||||
import { CategoryCollectionParseContextStub } from '../../../stubs/CategoryCollectionParseContextStub';
|
||||
import { LanguageSyntaxStub } from '../../../stubs/LanguageSyntaxStub';
|
||||
import { ScriptCompilerStub } from '@tests/unit/stubs/ScriptCompilerStub';
|
||||
import { ScriptDataStub } from '@tests/unit/stubs/ScriptDataStub';
|
||||
import { EnumParserStub } from '@tests/unit/stubs/EnumParserStub';
|
||||
import { ScriptCodeStub } from '@tests/unit/stubs/ScriptCodeStub';
|
||||
import { CategoryCollectionParseContextStub } from '@tests/unit/stubs/CategoryCollectionParseContextStub';
|
||||
import { LanguageSyntaxStub } from '@tests/unit/stubs/LanguageSyntaxStub';
|
||||
|
||||
describe('ScriptParser', () => {
|
||||
describe('parseScript', () => {
|
||||
|
||||
@@ -4,7 +4,6 @@ import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||
import { BatchFileSyntax } from '@/application/Parser/Script/Syntax/BatchFileSyntax';
|
||||
import { ShellScriptSyntax } from '@/application/Parser/Script/Syntax/ShellScriptSyntax';
|
||||
|
||||
|
||||
function getSystemsUnderTest(): ILanguageSyntax[] {
|
||||
return [ new BatchFileSyntax(), new ShellScriptSyntax() ];
|
||||
}
|
||||
|
||||
@@ -1,38 +1,14 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { SyntaxFactory } from '@/application/Parser/Script/Syntax/SyntaxFactory';
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { ShellScriptSyntax } from '@/application/Parser/Script/Syntax/ShellScriptSyntax';
|
||||
import { BatchFileSyntax } from '@/application/Parser/Script/Syntax/BatchFileSyntax';
|
||||
import { ScriptingLanguageFactoryTestRunner } from '@tests/unit/application/Common/ScriptingLanguage/ScriptingLanguageFactoryTestRunner';
|
||||
|
||||
describe('SyntaxFactory', () => {
|
||||
describe('getSyntax', () => {
|
||||
describe('creates expected type', () => {
|
||||
describe('shellscript returns ShellBuilder', () => {
|
||||
// arrange
|
||||
const testCases: Array< { language: ScriptingLanguage, expected: any} > = [
|
||||
{ language: ScriptingLanguage.shellscript, expected: ShellScriptSyntax},
|
||||
{ language: ScriptingLanguage.batchfile, expected: BatchFileSyntax},
|
||||
];
|
||||
for (const testCase of testCases) {
|
||||
it(ScriptingLanguage[testCase.language], () => {
|
||||
// act
|
||||
const sut = new SyntaxFactory();
|
||||
const result = sut.create(testCase.language);
|
||||
// assert
|
||||
expect(result).to.be.instanceOf(testCase.expected,
|
||||
`Actual was: ${result.constructor.name}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
it('throws on unknown scripting language', () => {
|
||||
// arrange
|
||||
const sut = new SyntaxFactory();
|
||||
// act
|
||||
const act = () => sut.create(3131313131);
|
||||
// assert
|
||||
expect(act).to.throw(`unknown language: "${ScriptingLanguage[3131313131]}"`);
|
||||
});
|
||||
});
|
||||
const sut = new SyntaxFactory();
|
||||
const runner = new ScriptingLanguageFactoryTestRunner()
|
||||
.expect(ScriptingLanguage.shellscript, ShellScriptSyntax)
|
||||
.expect(ScriptingLanguage.batchfile, BatchFileSyntax);
|
||||
runner.testCreateMethod(sut);
|
||||
});
|
||||
|
||||
@@ -2,8 +2,8 @@ import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { CodeSubstituter } from '@/application/Parser/ScriptingDefinition/CodeSubstituter';
|
||||
import { IExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/IExpressionsCompiler';
|
||||
import { ProjectInformationStub } from '../../../stubs/ProjectInformationStub';
|
||||
import { ExpressionsCompilerStub } from '../../../stubs/ExpressionsCompilerStub';
|
||||
import { ProjectInformationStub } from '@tests/unit/stubs/ProjectInformationStub';
|
||||
import { ExpressionsCompilerStub } from '@tests/unit/stubs/ExpressionsCompilerStub';
|
||||
|
||||
describe('CodeSubstituter', () => {
|
||||
describe('throws with invalid parameters', () => {
|
||||
|
||||
@@ -5,10 +5,10 @@ import { ScriptingDefinitionParser } from '@/application/Parser/ScriptingDefinit
|
||||
import { IEnumParser } from '@/application/Common/Enum';
|
||||
import { ICodeSubstituter } from '@/application/Parser/ScriptingDefinition/ICodeSubstituter';
|
||||
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
import { ProjectInformationStub } from '../../../stubs/ProjectInformationStub';
|
||||
import { EnumParserStub } from '../../../stubs/EnumParserStub';
|
||||
import { ScriptingDefinitionDataStub } from '../../../stubs/ScriptingDefinitionDataStub';
|
||||
import { CodeSubstituterStub } from '../../../stubs/CodeSubstituterStub';
|
||||
import { ProjectInformationStub } from '@tests/unit/stubs/ProjectInformationStub';
|
||||
import { EnumParserStub } from '@tests/unit/stubs/EnumParserStub';
|
||||
import { ScriptingDefinitionDataStub } from '@tests/unit/stubs/ScriptingDefinitionDataStub';
|
||||
import { CodeSubstituterStub } from '@tests/unit/stubs/CodeSubstituterStub';
|
||||
|
||||
describe('ScriptingDefinitionParser', () => {
|
||||
describe('parseScriptingDefinition', () => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { Application } from '@/domain/Application';
|
||||
import { CategoryCollectionStub } from '../stubs/CategoryCollectionStub';
|
||||
import { ProjectInformationStub } from '../stubs/ProjectInformationStub';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
|
||||
import { ProjectInformationStub } from '@tests/unit/stubs/ProjectInformationStub';
|
||||
|
||||
describe('Application', () => {
|
||||
describe('getCollection', () => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { Category } from '@/domain/Category';
|
||||
import { CategoryStub } from '../stubs/CategoryStub';
|
||||
import { ScriptStub } from '../stubs/ScriptStub';
|
||||
import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
|
||||
describe('Category', () => {
|
||||
describe('ctor', () => {
|
||||
|
||||
@@ -7,8 +7,9 @@ import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { RecommendationLevel } from '@/domain/RecommendationLevel';
|
||||
import { getEnumValues } from '@/application/Common/Enum';
|
||||
import { CategoryCollection } from '@/domain/CategoryCollection';
|
||||
import { ScriptStub } from '../stubs/ScriptStub';
|
||||
import { CategoryStub } from '../stubs/CategoryStub';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
|
||||
import { EnumRangeTestRunner } from '@tests/unit/application/Common/EnumRangeTestRunner';
|
||||
|
||||
describe('CategoryCollection', () => {
|
||||
describe('getScriptsByLevel', () => {
|
||||
@@ -186,35 +187,15 @@ describe('CategoryCollection', () => {
|
||||
// assert
|
||||
expect(sut.os).to.deep.equal(expected);
|
||||
});
|
||||
it('cannot construct with unknown os', () => {
|
||||
// arrange
|
||||
const os = OperatingSystem.Unknown;
|
||||
describe('throws when invalid', () => {
|
||||
// act
|
||||
const construct = () => new CategoryCollectionBuilder()
|
||||
const act = (os: OperatingSystem) => new CategoryCollectionBuilder()
|
||||
.withOs(os)
|
||||
.construct();
|
||||
// assert
|
||||
expect(construct).to.throw('unknown os');
|
||||
});
|
||||
it('cannot construct with undefined os', () => {
|
||||
// arrange
|
||||
const os = undefined;
|
||||
// act
|
||||
const construct = () => new CategoryCollectionBuilder()
|
||||
.withOs(os)
|
||||
.construct();
|
||||
// assert
|
||||
expect(construct).to.throw('undefined os');
|
||||
});
|
||||
it('cannot construct with OS not in range', () => {
|
||||
// arrange
|
||||
const os: OperatingSystem = 666;
|
||||
// act
|
||||
const construct = () => new CategoryCollectionBuilder()
|
||||
.withOs(os)
|
||||
.construct();
|
||||
// assert
|
||||
expect(construct).to.throw(`os "${os}" is out of range`);
|
||||
new EnumRangeTestRunner(act)
|
||||
.testOutOfRangeThrows()
|
||||
.testUndefinedValueThrows();
|
||||
});
|
||||
});
|
||||
describe('scriptingDefinition', () => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { ProjectInformation } from '@/domain/ProjectInformation';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { EnumRangeTestRunner } from '@tests/unit/application/Common/EnumRangeTestRunner';
|
||||
|
||||
describe('ProjectInformation', () => {
|
||||
it('sets name as expected', () => {
|
||||
@@ -115,14 +116,16 @@ describe('ProjectInformation', () => {
|
||||
// assert
|
||||
expect(actual).to.equal(expected);
|
||||
});
|
||||
it('throws when OS is unknown', () => {
|
||||
describe('throws when os is invalid', () => {
|
||||
// arrange
|
||||
const sut = new ProjectInformation('name', 'version', 'repositoryUrl', 'homepage');
|
||||
const os = OperatingSystem.Unknown;
|
||||
// act
|
||||
const act = () => sut.getDownloadUrl(os);
|
||||
const act = (os: OperatingSystem) => sut.getDownloadUrl(os);
|
||||
// assert
|
||||
expect(act).to.throw(`Unsupported os: ${OperatingSystem[os]}`);
|
||||
new EnumRangeTestRunner(act)
|
||||
.testOutOfRangeThrows()
|
||||
.testUndefinedValueThrows()
|
||||
.testInvalidValueThrows(OperatingSystem.KaiOS, `Unsupported os: ${OperatingSystem[OperatingSystem.KaiOS]}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { getEnumValues } from '@/application/Common/Enum';
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { getEnumValues } from '@/application/Common/Enum';
|
||||
import { Script } from '@/domain/Script';
|
||||
import { RecommendationLevel } from '@/domain/RecommendationLevel';
|
||||
import { IScriptCode } from '@/domain/IScriptCode';
|
||||
import { ScriptCodeStub } from '../stubs/ScriptCodeStub';
|
||||
import { ScriptCodeStub } from '@tests/unit/stubs/ScriptCodeStub';
|
||||
|
||||
describe('Script', () => {
|
||||
describe('ctor', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { expect } from 'chai';
|
||||
import { ScriptCode } from '@/domain/ScriptCode';
|
||||
import { IScriptCode } from '@/domain/IScriptCode';
|
||||
import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||
import { LanguageSyntaxStub } from '../stubs/LanguageSyntaxStub';
|
||||
import { LanguageSyntaxStub } from '@tests/unit/stubs/LanguageSyntaxStub';
|
||||
|
||||
describe('ScriptCode', () => {
|
||||
describe('code', () => {
|
||||
|
||||
@@ -1,28 +1,24 @@
|
||||
import { AsyncLazy } from '@/infrastructure/Threading/AsyncLazy';
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { AsyncLazy } from '@/infrastructure/Threading/AsyncLazy';
|
||||
|
||||
describe('AsyncLazy', () => {
|
||||
|
||||
it('returns value from lambda', async () => {
|
||||
// arrange
|
||||
const expected = 'test';
|
||||
const lambda = () => Promise.resolve(expected);
|
||||
const sut = new AsyncLazy(lambda);
|
||||
|
||||
// act
|
||||
const actual = await sut.getValueAsync();
|
||||
|
||||
// assert
|
||||
expect(actual).to.equal(expected);
|
||||
});
|
||||
|
||||
describe('when running multiple times', () => {
|
||||
// arrange
|
||||
let totalExecuted: number = 0;
|
||||
|
||||
beforeEach(() => totalExecuted = 0);
|
||||
|
||||
it('when running sync', async () => {
|
||||
// act
|
||||
const sut = new AsyncLazy(() => {
|
||||
totalExecuted++;
|
||||
return Promise.resolve(totalExecuted);
|
||||
@@ -31,11 +27,12 @@ describe('AsyncLazy', () => {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
results.push(await sut.getValueAsync());
|
||||
}
|
||||
// assert
|
||||
expect(totalExecuted).to.equal(1);
|
||||
expect(results).to.deep.equal([1, 1, 1, 1, 1]);
|
||||
});
|
||||
|
||||
it('when running long-running task in parallel', async () => {
|
||||
// act
|
||||
const sleepAsync = (time: number) => new Promise(((resolve) => setTimeout(resolve, time)));
|
||||
const sut = new AsyncLazy(async () => {
|
||||
await sleepAsync(100);
|
||||
@@ -48,6 +45,7 @@ describe('AsyncLazy', () => {
|
||||
sut.getValueAsync(),
|
||||
sut.getValueAsync(),
|
||||
sut.getValueAsync()]);
|
||||
// assert
|
||||
expect(totalExecuted).to.equal(1);
|
||||
expect(results).to.deep.equal([1, 1, 1, 1, 1]);
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { EnvironmentStub } from './../stubs/EnvironmentStub';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { runCodeAsync } from '@/infrastructure/CodeRunner';
|
||||
import { EnvironmentStub } from '@tests/unit/stubs/EnvironmentStub';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { CodeRunner } from '@/infrastructure/CodeRunner';
|
||||
|
||||
describe('CodeRunner', () => {
|
||||
describe('runCodeAsync', () => {
|
||||
@@ -127,7 +127,8 @@ class TestContext {
|
||||
private env = mockEnvironment(OperatingSystem.Windows);
|
||||
|
||||
public async runCodeAsync(): Promise<void> {
|
||||
await runCodeAsync(this.code, this.folderName, this.fileExtension, this.mocks, this.env);
|
||||
const runner = new CodeRunner(this.mocks, this.env);
|
||||
await runner.runCodeAsync(this.code, this.folderName, this.fileExtension);
|
||||
}
|
||||
public withOs(os: OperatingSystem) {
|
||||
this.env = mockEnvironment(os);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { EventHandler, IEventSource, IEventSubscription } from '@/infrastructure/Events/IEventSource';
|
||||
import { EventSource } from '@/infrastructure/Events/EventSource';
|
||||
import { expect } from 'chai';
|
||||
import 'mocha';
|
||||
|
||||
describe('EventSource', () => {
|
||||
class ObserverMock {
|
||||
@@ -42,7 +42,6 @@ describe('EventSource', () => {
|
||||
expect(observer.onReceiveCalls).to.have.lengthOf(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('multiple observers', () => {
|
||||
// arrange
|
||||
let observers: ObserverMock[];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { EventSubscriptionCollection } from '@/infrastructure/Events/EventSubscriptionCollection';
|
||||
import { IEventSubscription } from '@/infrastructure/Events/IEventSource';
|
||||
import { expect } from 'chai';
|
||||
import 'mocha';
|
||||
|
||||
describe('EventSubscriptionCollection', () => {
|
||||
it('unsubscribeAll unsubscribes from all registered subscriptions', () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { NumericEntityStub } from './../stubs/NumericEntityStub';
|
||||
import { InMemoryRepository } from '@/infrastructure/Repository/InMemoryRepository';
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { NumericEntityStub } from '@tests/unit/stubs/NumericEntityStub';
|
||||
import { InMemoryRepository } from '@/infrastructure/Repository/InMemoryRepository';
|
||||
|
||||
describe('InMemoryRepository', () => {
|
||||
describe('exists', () => {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { RecommendationLevel } from '@/domain/RecommendationLevel';
|
||||
import { CategoryCollectionStateStub } from '@tests/unit/stubs/CategoryCollectionStateStub';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
|
||||
export class SelectionStateTestScenario {
|
||||
public readonly all: readonly SelectedScript[];
|
||||
public readonly allStandard: readonly SelectedScript[];
|
||||
public readonly someStandard: readonly SelectedScript[];
|
||||
public readonly someStrict: readonly SelectedScript[];
|
||||
public readonly allStrict: readonly SelectedScript[];
|
||||
public readonly someUnrecommended: readonly SelectedScript[];
|
||||
public readonly allUnrecommended: readonly SelectedScript[];
|
||||
constructor() {
|
||||
this.someStandard = createSelectedScripts(RecommendationLevel.Standard, 'standard-some-1', 'standard-some-2');
|
||||
this.allStandard = [...this.someStandard, ...createSelectedScripts(RecommendationLevel.Standard, 'standard-all-1', 'standard-all-2')];
|
||||
this.someStrict = createSelectedScripts(RecommendationLevel.Strict, 'strict-some-1', 'strict-some-2');
|
||||
this.allStrict = [...this.someStrict, ...createSelectedScripts(RecommendationLevel.Strict, 'strict-all-1', 'strict-all-2')];
|
||||
this.someUnrecommended = createSelectedScripts(undefined, 'unrecommended-some-1', 'unrecommended-some-2');
|
||||
this.allUnrecommended = [...this.someUnrecommended, ...createSelectedScripts(undefined, 'unrecommended-all-1', 'unrecommended-all-2')];
|
||||
this.all = [...this.allStandard, ...this.allStrict, ...this.allUnrecommended];
|
||||
}
|
||||
public generateState(selectedScripts: readonly SelectedScript[]) {
|
||||
const allScripts = this.all.map((s) => s.script);
|
||||
return new CategoryCollectionStateStub(allScripts)
|
||||
.withSelectedScripts(selectedScripts);
|
||||
}
|
||||
}
|
||||
|
||||
function createSelectedScripts(level?: RecommendationLevel, ...ids: string[]) {
|
||||
return ids.map((id) => new SelectedScript(new ScriptStub(id).withLevel(level), false));
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { SelectionType, SelectionTypeHandler } from '@/presentation/components/Scripts/Menu/Selector/SelectionTypeHandler';
|
||||
import { scrambledEqual } from '@/application/Common/Array';
|
||||
import { RecommendationLevel } from '@/domain/RecommendationLevel';
|
||||
import { SelectionStateTestScenario } from './SelectionStateTestScenario';
|
||||
|
||||
describe('SelectionTypeHandler', () => {
|
||||
describe('ctor', () => {
|
||||
it('throws when state is undefined', () => {
|
||||
// arrange
|
||||
const expectedError = 'undefined state';
|
||||
const state = undefined;
|
||||
// act
|
||||
const sut = () => new SelectionTypeHandler(state);
|
||||
// assert
|
||||
expect(sut).to.throw(expectedError);
|
||||
});
|
||||
});
|
||||
describe('selectType', () => {
|
||||
it('throws when type is custom', () => {
|
||||
// arrange
|
||||
const expectedError = 'cannot select custom type';
|
||||
const scenario = new SelectionStateTestScenario();
|
||||
const state = scenario.generateState([]);
|
||||
const sut = new SelectionTypeHandler(state);
|
||||
// act
|
||||
const act = () => sut.selectType(SelectionType.Custom);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
describe('select types as expected', () => {
|
||||
// arrange
|
||||
const scenario = new SelectionStateTestScenario();
|
||||
const initialScriptsCases = [{
|
||||
name: 'when nothing is selected',
|
||||
initialScripts: [],
|
||||
}, {
|
||||
name: 'when some scripts are selected',
|
||||
initialScripts: [...scenario.allStandard, ...scenario.someStrict],
|
||||
}, {
|
||||
name: 'when all scripts are selected',
|
||||
initialScripts: scenario.all,
|
||||
} ];
|
||||
for (const initialScriptsCase of initialScriptsCases) {
|
||||
describe(initialScriptsCase.name, () => {
|
||||
const state = scenario.generateState(initialScriptsCase.initialScripts);
|
||||
const sut = new SelectionTypeHandler(state);
|
||||
const typeExpectations = [{
|
||||
input: SelectionType.None,
|
||||
output: [],
|
||||
}, {
|
||||
input: SelectionType.Standard,
|
||||
output: scenario.allStandard,
|
||||
}, {
|
||||
input: SelectionType.Strict,
|
||||
output: [...scenario.allStandard, ...scenario.allStrict],
|
||||
}, {
|
||||
input: SelectionType.All,
|
||||
output: scenario.all,
|
||||
}];
|
||||
for (const expectation of typeExpectations) {
|
||||
// act
|
||||
it(`${SelectionType[expectation.input]} returns as expected`, () => {
|
||||
sut.selectType(expectation.input);
|
||||
// assert
|
||||
const actual = state.selection.selectedScripts;
|
||||
const expected = expectation.output;
|
||||
expect(scrambledEqual(actual, expected));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('getCurrentSelectionType', () => {
|
||||
// arrange
|
||||
const scenario = new SelectionStateTestScenario();
|
||||
const testCases = [{
|
||||
name: 'when nothing is selected',
|
||||
selection: [],
|
||||
expected: SelectionType.None,
|
||||
}, {
|
||||
name: 'when some standard scripts are selected',
|
||||
selection: scenario.someStandard,
|
||||
expected: SelectionType.Custom,
|
||||
}, {
|
||||
name: 'when all standard scripts are selected',
|
||||
selection: scenario.allStandard,
|
||||
expected: SelectionType.Standard,
|
||||
}, {
|
||||
name: 'when all standard and some strict scripts are selected',
|
||||
selection: [...scenario.allStandard, ...scenario.someStrict],
|
||||
expected: SelectionType.Custom,
|
||||
}, {
|
||||
name: 'when all standard and strict scripts are selected',
|
||||
selection: [...scenario.allStandard, ...scenario.allStrict],
|
||||
expected: SelectionType.Strict,
|
||||
}, {
|
||||
name: 'when strict scripts are selected but not standard',
|
||||
selection: scenario.allStrict,
|
||||
expected: SelectionType.Custom,
|
||||
}, {
|
||||
name: 'when all standard and strict, and some unrecommended are selected',
|
||||
selection: [...scenario.allStandard, ...scenario.allStrict, ...scenario.someUnrecommended],
|
||||
expected: SelectionType.Custom,
|
||||
}, {
|
||||
name: 'when all scripts are selected',
|
||||
selection: scenario.all,
|
||||
expected: SelectionType.All,
|
||||
} ];
|
||||
for (const testCase of testCases) {
|
||||
it(testCase.name, () => {
|
||||
const state = scenario.generateState(testCase.selection);
|
||||
const sut = new SelectionTypeHandler(state);
|
||||
// act
|
||||
const actual = sut.getCurrentSelectionType();
|
||||
// assert
|
||||
expect(actual).to.deep.equal(testCase.expected,
|
||||
`Actual: "${SelectionType[actual]}", expected: "${SelectionType[testCase.expected]}"` +
|
||||
`\nSelection: ${printSelection()}`);
|
||||
function printSelection() {
|
||||
return `total: ${testCase.selection.length}\n` +
|
||||
'scripts:\n' +
|
||||
testCase.selection
|
||||
.map((s) => `{ id: ${s.script.id}, level: ${s.script.level === undefined ? 'unknown' : RecommendationLevel[s.script.level]} }`)
|
||||
.join(' | ');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -6,9 +6,9 @@ import { parseSingleCategory,
|
||||
import { INode, NodeType } from '@/presentation/components/Scripts/ScriptsTree/SelectableTree/Node/INode';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { ICategory } from '@/domain/ICategory';
|
||||
import { CategoryStub } from '../../../../stubs/CategoryStub';
|
||||
import { ScriptStub } from '../../../../stubs/ScriptStub';
|
||||
import { CategoryCollectionStub } from '../../../../stubs/CategoryCollectionStub';
|
||||
import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
|
||||
|
||||
describe('ScriptNodeParser', () => {
|
||||
it('can convert script id and back', () => {
|
||||
|
||||
@@ -4,9 +4,9 @@ import { CategoryReverter } from '@/presentation/components/Scripts/ScriptsTree/
|
||||
import { getCategoryNodeId } from '@/presentation/components/Scripts/ScriptsTree/ScriptNodeParser';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
|
||||
import { CategoryStub } from '../../../../../../../stubs/CategoryStub';
|
||||
import { CategoryCollectionStub } from '../../../../../../../stubs/CategoryCollectionStub';
|
||||
import { ScriptStub } from '../../../../../../../stubs/ScriptStub';
|
||||
import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
|
||||
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
|
||||
describe('CategoryReverter', () => {
|
||||
describe('getState', () => {
|
||||
|
||||
@@ -6,9 +6,9 @@ import {
|
||||
import { ScriptReverter } from '@/presentation/components/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ScriptReverter';
|
||||
import { CategoryReverter } from '@/presentation/components/Scripts/ScriptsTree/SelectableTree/Node/Reverter/CategoryReverter';
|
||||
import { getScriptNodeId, getCategoryNodeId } from '@/presentation/components/Scripts/ScriptsTree/ScriptNodeParser';
|
||||
import { CategoryCollectionStub } from '../../../../../../../stubs/CategoryCollectionStub';
|
||||
import { CategoryStub } from '../../../../../../../stubs/CategoryStub';
|
||||
import { ScriptStub } from '../../../../../../../stubs/ScriptStub';
|
||||
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
|
||||
import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
|
||||
describe('ReverterFactory', () => {
|
||||
describe('getReverter', () => {
|
||||
|
||||
@@ -4,10 +4,10 @@ import { ScriptReverter } from '@/presentation/components/Scripts/ScriptsTree/Se
|
||||
import { getScriptNodeId } from '@/presentation/components/Scripts/ScriptsTree/ScriptNodeParser';
|
||||
import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { CategoryCollectionStub } from '../../../../../../../stubs/CategoryCollectionStub';
|
||||
import { CategoryStub } from '../../../../../../../stubs/CategoryStub';
|
||||
import { ScriptStub } from '../../../../../../../stubs/ScriptStub';
|
||||
import { SelectedScriptStub } from '../../../../../../../stubs/SelectedScriptStub';
|
||||
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
|
||||
import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
import { SelectedScriptStub } from '@tests/unit/stubs/SelectedScriptStub';
|
||||
|
||||
describe('ScriptReverter', () => {
|
||||
describe('getState', () => {
|
||||
|
||||
9
tests/unit/stubs/ApplicationCodeStub.ts
Normal file
9
tests/unit/stubs/ApplicationCodeStub.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { ICodeChangedEvent } from '@/application/Context/State/Code/Event/ICodeChangedEvent';
|
||||
import { IApplicationCode } from '@/application/Context/State/Code/IApplicationCode';
|
||||
import { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||
import { EventSource } from '@/infrastructure/Events/EventSource';
|
||||
|
||||
export class ApplicationCodeStub implements IApplicationCode {
|
||||
public changed: IEventSource<ICodeChangedEvent> = new EventSource<ICodeChangedEvent>();
|
||||
public current: string = '';
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ICategoryCollectionParseContext } from '@/application/Parser/Script/ICategoryCollectionParseContext';
|
||||
import { ScriptCompilerStub } from './ScriptCompilerStub';
|
||||
import { LanguageSyntaxStub } from './LanguageSyntaxStub';
|
||||
import { IScriptCompiler } from '@/application/Parser/Script/Compiler/IScriptCompiler';
|
||||
import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||
import { ScriptCompilerStub } from './ScriptCompilerStub';
|
||||
import { LanguageSyntaxStub } from './LanguageSyntaxStub';
|
||||
|
||||
export class CategoryCollectionParseContextStub implements ICategoryCollectionParseContext {
|
||||
public compiler: IScriptCompiler = new ScriptCompilerStub();
|
||||
|
||||
30
tests/unit/stubs/CategoryCollectionStateStub.ts
Normal file
30
tests/unit/stubs/CategoryCollectionStateStub.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { IApplicationCode } from '@/application/Context/State/Code/IApplicationCode';
|
||||
import { IUserFilter } from '@/application/Context/State/Filter/IUserFilter';
|
||||
import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { CategoryCollectionStub } from './CategoryCollectionStub';
|
||||
import { UserSelectionStub } from './UserSelectionStub';
|
||||
import { UserFilterStub } from './UserFilterStub';
|
||||
import { ApplicationCodeStub } from './ApplicationCodeStub';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { CategoryStub } from './CategoryStub';
|
||||
|
||||
export class CategoryCollectionStateStub implements ICategoryCollectionState {
|
||||
public readonly code: IApplicationCode = new ApplicationCodeStub();
|
||||
public readonly filter: IUserFilter = new UserFilterStub();
|
||||
public readonly os = OperatingSystem.Windows;
|
||||
public readonly collection: CategoryCollectionStub;
|
||||
public readonly selection: UserSelectionStub;
|
||||
constructor(readonly allScripts: IScript[]) {
|
||||
this.selection = new UserSelectionStub(allScripts);
|
||||
this.collection = new CategoryCollectionStub()
|
||||
.withOs(this.os)
|
||||
.withTotalScripts(this.allScripts.length)
|
||||
.withAction(new CategoryStub(0).withScripts(...allScripts));
|
||||
}
|
||||
public withSelectedScripts(initialScripts: readonly SelectedScript[]) {
|
||||
this.selection.withSelectedScripts(initialScripts);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { ScriptingDefinitionStub } from './ScriptingDefinitionStub';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { ScriptStub } from './ScriptStub';
|
||||
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { ICategory } from '@/domain/ICategory';
|
||||
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import { ScriptStub } from './ScriptStub';
|
||||
import { ScriptingDefinitionStub } from './ScriptingDefinitionStub';
|
||||
import { RecommendationLevel } from '@/domain/RecommendationLevel';
|
||||
|
||||
export class CategoryCollectionStub implements ICategoryCollection {
|
||||
public scripting: IScriptingDefinition = new ScriptingDefinitionStub();
|
||||
@@ -36,14 +37,16 @@ export class CategoryCollectionStub implements ICategoryCollection {
|
||||
}
|
||||
|
||||
public findCategory(categoryId: number): ICategory {
|
||||
return this.getAllCategories().find(
|
||||
(category) => category.id === categoryId);
|
||||
return this.getAllCategories()
|
||||
.find((category) => category.id === categoryId);
|
||||
}
|
||||
public getScriptsByLevel(): readonly IScript[] {
|
||||
throw new Error('Method not implemented: getScriptsByLevel');
|
||||
public getScriptsByLevel(level: RecommendationLevel): readonly IScript[] {
|
||||
return this.getAllScripts()
|
||||
.filter((script) => script.level !== undefined && script.level <= level);
|
||||
}
|
||||
public findScript(scriptId: string): IScript {
|
||||
return this.getAllScripts().find((script) => scriptId === script.id);
|
||||
return this.getAllScripts()
|
||||
.find((script) => scriptId === script.id);
|
||||
}
|
||||
public getAllScripts(): ReadonlyArray<IScript> {
|
||||
const scripts = [];
|
||||
@@ -79,9 +82,7 @@ function getSubCategoriesRecursively(category: ICategory): ReadonlyArray<ICatego
|
||||
function getScriptsRecursively(category: ICategory): ReadonlyArray<IScript> {
|
||||
const categoryScripts = [];
|
||||
if (category.scripts) {
|
||||
for (const script of category.scripts) {
|
||||
categoryScripts.push(script);
|
||||
}
|
||||
categoryScripts.push(...category.scripts);
|
||||
}
|
||||
if (category.subCategories) {
|
||||
for (const subCategory of category.subCategories) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user