Compare commits
9 Commits
0.11.1
...
disableser
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c8412c467 | ||
|
|
7c02ffb6c9 | ||
|
|
f2d9881382 | ||
|
|
d7761ab30e | ||
|
|
bf83c58982 | ||
|
|
2e082932c9 | ||
|
|
2f90cac52a | ||
|
|
20a0071c0d | ||
|
|
a40f83d6b6 |
11
CHANGELOG.md
11
CHANGELOG.md
@@ -1,5 +1,16 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.11.1 (2021-11-04)
|
||||||
|
|
||||||
|
* Update dependencies | [64631a4](https://github.com/undergroundwires/privacy.sexy/commit/64631a4552fad7f7b06286aba8d3ca2d731f9342)
|
||||||
|
* Fix, document, unrecommend Windows browser cleanup | [5ead1a0](https://github.com/undergroundwires/privacy.sexy/commit/5ead1a087d91948890bc4ae6fea176123f18c285)
|
||||||
|
* Fix failing URL status checking integration tests | [799fb09](https://github.com/undergroundwires/privacy.sexy/commit/799fb091b8eb06c70ac0c67f2ef5385dce73501f)
|
||||||
|
* Refactor to remove "Async" function name suffix | [82c43ba](https://github.com/undergroundwires/privacy.sexy/commit/82c43ba2e37fb6e7f62ccd9bec8c5f48575f0613)
|
||||||
|
* Fix dead URLs and use forks as GitHub references | [97ddc02](https://github.com/undergroundwires/privacy.sexy/commit/97ddc027cb5395a74991cabc1d8c875ee945636d)
|
||||||
|
* Fix website not loading on Safari | [0db8cc4](https://github.com/undergroundwires/privacy.sexy/commit/0db8cc420655e01cbbed57c4658489b761a15899)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.11.0...0.11.1)
|
||||||
|
|
||||||
## 0.11.0 (2021-10-21)
|
## 0.11.0 (2021-10-21)
|
||||||
|
|
||||||
* Change "grouping" to "view" | [c0c475f](https://github.com/undergroundwires/privacy.sexy/commit/c0c475ff564b23a4dabcc03ac2909207a8eb61ce)
|
* Change "grouping" to "view" | [c0c475f](https://github.com/undergroundwires/privacy.sexy/commit/c0c475ff564b23a4dabcc03ac2909207a8eb61ce)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
- Online version at [https://privacy.sexy](https://privacy.sexy)
|
- Online version at [https://privacy.sexy](https://privacy.sexy)
|
||||||
- 💡 No need to run any compiled software on your computer.
|
- 💡 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.11.0/privacy.sexy-Setup-0.11.0.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.11.0/privacy.sexy-0.11.0.dmg) or [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.11.0/privacy.sexy-0.11.0.AppImage).
|
- Alternatively download offline version for [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.11.1/privacy.sexy-Setup-0.11.1.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.11.1/privacy.sexy-0.11.1.dmg) or [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.11.1/privacy.sexy-0.11.1.AppImage).
|
||||||
- 💡 Single click to execute your script.
|
- 💡 Single click to execute your script.
|
||||||
- ❗ Come back regularly to apply latest version for stronger privacy and security.
|
- ❗ Come back regularly to apply latest version for stronger privacy and security.
|
||||||
|
|
||||||
@@ -57,8 +57,8 @@
|
|||||||
- Development: `npm run serve` to compile & hot-reload for development.
|
- Development: `npm run serve` to compile & hot-reload for development.
|
||||||
- Production: `npm run build` to prepare files for distribution.
|
- Production: `npm run build` to prepare files for distribution.
|
||||||
- Or run using Docker:
|
- Or run using Docker:
|
||||||
1. Build: `docker build -t undergroundwires/privacy.sexy:0.11.0 .`
|
1. Build: `docker build -t undergroundwires/privacy.sexy:0.11.1 .`
|
||||||
2. Run: `docker run -it -p 8080:80 --rm --name privacy.sexy-0.11.0 undergroundwires/privacy.sexy:0.11.0`
|
2. Run: `docker run -it -p 8080:80 --rm --name privacy.sexy-0.11.1 undergroundwires/privacy.sexy:0.11.1`
|
||||||
|
|
||||||
## Architecture overview
|
## Architecture overview
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,16 @@
|
|||||||
- [**`bootstrapping/`**](./../src/presentation/bootstrapping/): Registers Vue global objects including components and plugins.
|
- [**`bootstrapping/`**](./../src/presentation/bootstrapping/): Registers Vue global objects including components and plugins.
|
||||||
- [**`components/`**](./../src/presentation/components/): Contains all Vue components and their helper classes.
|
- [**`components/`**](./../src/presentation/components/): Contains all Vue components and their helper classes.
|
||||||
- [**`Shared/`**](./../src/presentation/components/Shared): Contains Vue components and component helpers that are shared across other components.
|
- [**`Shared/`**](./../src/presentation/components/Shared): Contains Vue components and component helpers that are shared across other components.
|
||||||
- [**`styles/`**](./../src/presentation/styles/): Contains shared styles used throughout different components.
|
- [**`assets/`**](./../src/presentation/assets/styles/): Contains assets that will be processed by webpack.
|
||||||
|
- [**`fonts/`**](./../src/presentation/assets/fonts/): Contains fonts
|
||||||
|
- [**`styles/`**](./../src/presentation/assets/styles/): Contains shared styles used throughout different components.
|
||||||
|
- [**`components/`**](./../src/presentation/assets/styles/components): Contains styles that are reusable and tightly coupled a Vue/HTML component.
|
||||||
|
- [**`vendors-extensions/`**](./../src/presentation/assets/styles/third-party-extensions): Contains styles that override third-party components used.
|
||||||
|
- [**`main.scss`**](./../src/presentation/assets/styles/main.scss): Primary Sass file, passes along all other styles, should be the only file used from other components.
|
||||||
- [**`main.ts`**](./../src/presentation/main.ts): Application entry point that mounts and starts Vue application.
|
- [**`main.ts`**](./../src/presentation/main.ts): Application entry point that mounts and starts Vue application.
|
||||||
- [`electron/`](./../src/presentation/electron/): Electron configuration for the desktop application.
|
- [**`electron/`**](./../src/presentation/electron/): Electron configuration for the desktop application.
|
||||||
- [**`main.ts`**](./../src/presentation/main.ts): Main process of Electron, started as first thing when app starts.
|
- [**`main.ts`**](./../src/presentation/main.ts): Main process of Electron, started as first thing when app starts.
|
||||||
- [**`/public/`**](./../public/): Contains static assets that will simply be copied and not go through webpack.
|
- [**`/public/`**](./../public/): Contains static assets that will directly be copied and not go through webpack.
|
||||||
- [**`/vue.config.js`**](./../vue.config.js): Global Vue CLI configurations loaded by `@vue/cli-service`
|
- [**`/vue.config.js`**](./../vue.config.js): Global Vue CLI configurations loaded by `@vue/cli-service`
|
||||||
- [**`/postcss.config.js`**](./../postcss.config.js): PostCSS configurations that are used by Vue CLI internally
|
- [**`/postcss.config.js`**](./../postcss.config.js): PostCSS configurations that are used by Vue CLI internally
|
||||||
- [**`/babel.config.js`**](./../babel.config.js): Babel configurations for polyfills used by `@vue/cli-plugin-babel`
|
- [**`/babel.config.js`**](./../babel.config.js): Babel configurations for polyfills used by `@vue/cli-plugin-babel`
|
||||||
@@ -50,3 +55,18 @@
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
<div @click="$refs.testDialog.show()">Show dialog</div>
|
<div @click="$refs.testDialog.show()">Show dialog</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Sass naming convention
|
||||||
|
|
||||||
|
- Use lowercase for variables/functions/mixins e.g.
|
||||||
|
- Variable: `$variable: value;`
|
||||||
|
- Function: `@function function() {}`
|
||||||
|
- Mixin: `@mixin mixin() {}`
|
||||||
|
- Use - for a phrase/compound word e.g.
|
||||||
|
- Variable: `$some-variable: value;`
|
||||||
|
- Function: `@function some-function() {}`
|
||||||
|
- Mixin: `@mixin some-mixin() {}`
|
||||||
|
- Grouping and name variables from generic to specific e.g.
|
||||||
|
- ✅ `$border-blue`, `$border-blue-light`, `$border-blue-lightest`, `$border-red`
|
||||||
|
- ❌ `$blue-border`, `$light-blue-border`, `$lightest-blue-border`, `$red-border`
|
||||||
|
|
||||||
145
package-lock.json
generated
145
package-lock.json
generated
@@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "privacy.sexy",
|
"name": "privacy.sexy",
|
||||||
"version": "0.11.0",
|
"version": "0.11.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"version": "0.11.0",
|
"name": "privacy.sexy",
|
||||||
|
"version": "0.11.1",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.36",
|
"@fortawesome/fontawesome-svg-core": "^1.2.36",
|
||||||
@@ -45,6 +46,7 @@
|
|||||||
"electron-updater": "^4.3.9",
|
"electron-updater": "^4.3.9",
|
||||||
"js-yaml-loader": "^1.2.2",
|
"js-yaml-loader": "^1.2.2",
|
||||||
"markdownlint-cli": "^0.29.0",
|
"markdownlint-cli": "^0.29.0",
|
||||||
|
"raw-loader": "^4.0.2",
|
||||||
"remark-cli": "^10.0.0",
|
"remark-cli": "^10.0.0",
|
||||||
"remark-lint-no-dead-urls": "^1.1.0",
|
"remark-lint-no-dead-urls": "^1.1.0",
|
||||||
"remark-preset-lint-consistent": "^5.1.0",
|
"remark-preset-lint-consistent": "^5.1.0",
|
||||||
@@ -2266,9 +2268,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/json-schema": {
|
"node_modules/@types/json-schema": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
|
||||||
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
|
"integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/mdast": {
|
"node_modules/@types/mdast": {
|
||||||
@@ -18684,6 +18686,82 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/raw-loader": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"loader-utils": "^2.0.0",
|
||||||
|
"schema-utils": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.13.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/webpack"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"webpack": "^4.0.0 || ^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/raw-loader/node_modules/emojis-list": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/raw-loader/node_modules/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,
|
||||||
|
"dependencies": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"json5": "lib/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/raw-loader/node_modules/loader-utils": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"big.js": "^5.2.2",
|
||||||
|
"emojis-list": "^3.0.0",
|
||||||
|
"json5": "^2.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/raw-loader/node_modules/schema-utils": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/json-schema": "^7.0.8",
|
||||||
|
"ajv": "^6.12.5",
|
||||||
|
"ajv-keywords": "^3.5.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.13.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/webpack"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/rc": {
|
"node_modules/rc": {
|
||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||||
@@ -27658,9 +27736,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/json-schema": {
|
"@types/json-schema": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
|
||||||
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
|
"integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/mdast": {
|
"@types/mdast": {
|
||||||
@@ -27936,6 +28014,7 @@
|
|||||||
"integrity": "sha512-P13AJv5FDt2XnpZ92K0VMxBS7Pe+gnibxtXMsa8rXLBkEE1NkmtaG5pyXh3fulkmF2/21efOcuh6yFP7k0KuZg==",
|
"integrity": "sha512-P13AJv5FDt2XnpZ92K0VMxBS7Pe+gnibxtXMsa8rXLBkEE1NkmtaG5pyXh3fulkmF2/21efOcuh6yFP7k0KuZg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"@babel/core": "^7.11.0",
|
||||||
"@babel/helper-compilation-targets": "^7.9.6",
|
"@babel/helper-compilation-targets": "^7.9.6",
|
||||||
"@babel/helper-module-imports": "^7.8.3",
|
"@babel/helper-module-imports": "^7.8.3",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||||
@@ -27948,6 +28027,7 @@
|
|||||||
"@vue/babel-plugin-jsx": "^1.0.3",
|
"@vue/babel-plugin-jsx": "^1.0.3",
|
||||||
"@vue/babel-preset-jsx": "^1.2.4",
|
"@vue/babel-preset-jsx": "^1.2.4",
|
||||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||||
|
"core-js": "^3.6.5",
|
||||||
"core-js-compat": "^3.6.5",
|
"core-js-compat": "^3.6.5",
|
||||||
"semver": "^6.1.0"
|
"semver": "^6.1.0"
|
||||||
}
|
}
|
||||||
@@ -40396,6 +40476,55 @@
|
|||||||
"unpipe": "1.0.0"
|
"unpipe": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"raw-loader": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"loader-utils": "^2.0.0",
|
||||||
|
"schema-utils": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"emojis-list": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
||||||
|
"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,
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"loader-utils": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"big.js": "^5.2.2",
|
||||||
|
"emojis-list": "^3.0.0",
|
||||||
|
"json5": "^2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schema-utils": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/json-schema": "^7.0.8",
|
||||||
|
"ajv": "^6.12.5",
|
||||||
|
"ajv-keywords": "^3.5.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"rc": {
|
"rc": {
|
||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "privacy.sexy",
|
"name": "privacy.sexy",
|
||||||
"version": "0.11.0",
|
"version": "0.11.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Enforce privacy & security best-practices on Windows and macOS, because privacy is sexy 🍑🍆",
|
"description": "Enforce privacy & security best-practices on Windows and macOS, because privacy is sexy 🍑🍆",
|
||||||
"author": "undergroundwires",
|
"author": "undergroundwires",
|
||||||
@@ -59,6 +59,7 @@
|
|||||||
"electron-updater": "^4.3.9",
|
"electron-updater": "^4.3.9",
|
||||||
"js-yaml-loader": "^1.2.2",
|
"js-yaml-loader": "^1.2.2",
|
||||||
"markdownlint-cli": "^0.29.0",
|
"markdownlint-cli": "^0.29.0",
|
||||||
|
"raw-loader": "^4.0.2",
|
||||||
"remark-cli": "^10.0.0",
|
"remark-cli": "^10.0.0",
|
||||||
"remark-lint-no-dead-urls": "^1.1.0",
|
"remark-lint-no-dead-urls": "^1.1.0",
|
||||||
"remark-preset-lint-consistent": "^5.1.0",
|
"remark-preset-lint-consistent": "^5.1.0",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 147 KiB After Width: | Height: | Size: 147 KiB |
@@ -5,13 +5,13 @@
|
|||||||
font-family: 'Slabo 27px';
|
font-family: 'Slabo 27px';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: url('~@/presentation/styles/fonts/slabo-27px-v6-latin-ext_latin-regular.eot'); /* IE9 Compat Modes */
|
src: url('~@/presentation/assets/fonts/slabo-27px-v6-latin-ext_latin-regular.eot'); /* IE9 Compat Modes */
|
||||||
src: local('Slabo 27px'), local('Slabo27px-Regular'),
|
src: local('Slabo 27px'), local('Slabo27px-Regular'),
|
||||||
url('~@/presentation/styles/fonts/slabo-27px-v6-latin-ext_latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
url('~@/presentation/assets/fonts/slabo-27px-v6-latin-ext_latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||||
url('~@/presentation/styles/fonts/slabo-27px-v6-latin-ext_latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
|
url('~@/presentation/assets/fonts/slabo-27px-v6-latin-ext_latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
|
||||||
url('~@/presentation/styles/fonts/slabo-27px-v6-latin-ext_latin-regular.woff') format('woff'), /* Modern Browsers */
|
url('~@/presentation/assets/fonts/slabo-27px-v6-latin-ext_latin-regular.woff') format('woff'), /* Modern Browsers */
|
||||||
url('~@/presentation/styles/fonts/slabo-27px-v6-latin-ext_latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
|
url('~@/presentation/assets/fonts/slabo-27px-v6-latin-ext_latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
|
||||||
url('~@/presentation/styles/fonts/slabo-27px-v6-latin-ext_latin-regular.svg#Slabo27px') format('svg'); /* Legacy iOS */
|
url('~@/presentation/assets/fonts/slabo-27px-v6-latin-ext_latin-regular.svg#Slabo27px') format('svg'); /* Legacy iOS */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* yesteryear-regular - latin */
|
/* yesteryear-regular - latin */
|
||||||
@@ -19,15 +19,15 @@
|
|||||||
font-family: 'Yesteryear';
|
font-family: 'Yesteryear';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: url('~@/presentation/styles/fonts/yesteryear-v8-latin-regular.eot'); /* IE9 Compat Modes */
|
src: url('~@/presentation/assets/fonts/yesteryear-v8-latin-regular.eot'); /* IE9 Compat Modes */
|
||||||
src: local('Yesteryear'), local('Yesteryear-Regular'),
|
src: local('Yesteryear'), local('Yesteryear-Regular'),
|
||||||
url('~@/presentation/styles/fonts/yesteryear-v8-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
url('~@/presentation/assets/fonts/yesteryear-v8-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||||
url('~@/presentation/styles/fonts/yesteryear-v8-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
|
url('~@/presentation/assets/fonts/yesteryear-v8-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
|
||||||
url('~@/presentation/styles/fonts/yesteryear-v8-latin-regular.woff') format('woff'), /* Modern Browsers */
|
url('~@/presentation/assets/fonts/yesteryear-v8-latin-regular.woff') format('woff'), /* Modern Browsers */
|
||||||
url('~@/presentation/styles/fonts/yesteryear-v8-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
|
url('~@/presentation/assets/fonts/yesteryear-v8-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
|
||||||
url('~@/presentation/styles/fonts/yesteryear-v8-latin-regular.svg#Yesteryear') format('svg'); /* Legacy iOS */
|
url('~@/presentation/assets/fonts/yesteryear-v8-latin-regular.svg#Yesteryear') format('svg'); /* Legacy iOS */
|
||||||
}
|
}
|
||||||
|
|
||||||
$normal-font: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;
|
$font-normal : 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;
|
||||||
$artistic-font: 'Yesteryear', cursive;
|
$font-artistic : 'Yesteryear', cursive;
|
||||||
$main-font: 'Slabo 27px';
|
$font-main : 'Slabo 27px';
|
||||||
25
src/presentation/assets/styles/_globals.scss
Normal file
25
src/presentation/assets/styles/_globals.scss
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
Defines global styles that applies to globally defined tags by default (body, main, article, div etc.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
@use "@/presentation/assets/styles/colors" as *;
|
||||||
|
@use "@/presentation/assets/styles/fonts" as *;
|
||||||
|
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color:inherit;
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
color: $color-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: $color-background;
|
||||||
|
font-family: $font-main;
|
||||||
|
}
|
||||||
5
src/presentation/assets/styles/_media.scss
Normal file
5
src/presentation/assets/styles/_media.scss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
$media-screen-big-width : 992px;
|
||||||
|
$media-screen-medium-width : 768px;
|
||||||
|
$media-screen-small-width : 380px;
|
||||||
|
|
||||||
|
$media-vertical-view-breakpoint : 992px;
|
||||||
11
src/presentation/assets/styles/main.scss
Normal file
11
src/presentation/assets/styles/main.scss
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/* This class is not supposed to more than forwarding other styles */
|
||||||
|
|
||||||
|
@forward "./fonts";
|
||||||
|
@forward "./media";
|
||||||
|
@forward "./colors";
|
||||||
|
@forward "./globals";
|
||||||
|
|
||||||
|
@forward "./components/card";
|
||||||
|
|
||||||
|
@forward "./third-party-extensions/tooltip.scss";
|
||||||
|
@forward "./third-party-extensions/tree.scss";
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// Based on https://github.com/Akryum/v-tooltip/blob/83615e394c96ca491a4df04b892ae87e833beb97/demo-src/src/App.vue#L179-L303
|
// Based on https://github.com/Akryum/v-tooltip/blob/83615e394c96ca491a4df04b892ae87e833beb97/demo-src/src/App.vue#L179-L303
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/colors" as *;
|
||||||
|
|
||||||
.tooltip {
|
.tooltip {
|
||||||
display: block !important;
|
display: block !important;
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// Overrides base styling for LiquorTree
|
||||||
|
@use "@/presentation/assets/styles/colors" as *;
|
||||||
|
|
||||||
|
$color-tree-bg : $color-primary-darker;
|
||||||
|
$color-node-arrow : $color-on-primary;
|
||||||
|
$color-node-fg : $color-on-primary;
|
||||||
|
$color-node-hover-bg : $color-primary-dark;
|
||||||
|
$color-node-keyboard-bg : $color-surface;
|
||||||
|
$color-node-keyboard-fg : $color-on-surface;
|
||||||
|
$color-node-checkbox-bg-checked : $color-secondary;
|
||||||
|
$color-node-checkbox-bg-unchecked : $color-primary-darkest;
|
||||||
|
$color-node-checkbox-border-checked : $color-secondary;
|
||||||
|
$color-node-checkbox-border-unchecked : $color-on-primary;
|
||||||
|
$color-node-checkbox-tick-checked : $color-on-secondary;
|
||||||
|
|
||||||
|
.tree {
|
||||||
|
background: $color-tree-bg;
|
||||||
|
&-node {
|
||||||
|
white-space: normal !important;
|
||||||
|
> .tree-content {
|
||||||
|
> .tree-anchor > span {
|
||||||
|
color: $color-node-fg;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
background: $color-node-hover-bg !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.selected { // When using keyboard navigation it highlights current item and its child items
|
||||||
|
background: $color-node-keyboard-bg;
|
||||||
|
.tree-text {
|
||||||
|
color: $color-node-keyboard-fg !important; // $block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-checkbox {
|
||||||
|
border-color: $color-node-checkbox-border-unchecked !important;
|
||||||
|
&.checked {
|
||||||
|
background: $color-node-checkbox-bg-checked !important;
|
||||||
|
border-color: $color-node-checkbox-border-checked !important;
|
||||||
|
&:after {
|
||||||
|
border-color: $color-node-checkbox-tick-checked !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.indeterminate {
|
||||||
|
border-color: $color-node-checkbox-border-unchecked !important;
|
||||||
|
}
|
||||||
|
background: $color-node-checkbox-bg-unchecked !important;
|
||||||
|
}
|
||||||
|
&-arrow {
|
||||||
|
&.has-child {
|
||||||
|
&.rtl:after, &:after {
|
||||||
|
border-color: $color-node-arrow !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,27 +33,7 @@ export default class App extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
@import "@/presentation/styles/fonts.scss";
|
|
||||||
@import "@/presentation/styles/media.scss";
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color:inherit;
|
|
||||||
text-decoration: underline;
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
color: $color-primary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: $color-background;
|
|
||||||
font-family: $main-font;
|
|
||||||
}
|
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
@@ -76,6 +56,4 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@import "@/presentation/styles/tooltip.scss";
|
|
||||||
@import "@/presentation/styles/tree.scss";
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -25,13 +25,12 @@ export default class Code extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
@import "@/presentation/styles/fonts.scss";
|
|
||||||
|
|
||||||
.code-wrapper {
|
.code-wrapper {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
font-family: $normal-font;
|
font-family: $font-normal;
|
||||||
background-color: $color-primary-darker;
|
background-color: $color-primary-darker;
|
||||||
color: $color-on-primary;
|
color: $color-on-primary;
|
||||||
padding-left: 0.3rem;
|
padding-left: 0.3rem;
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ export default class IconButton extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
@import "@/presentation/styles/fonts.scss";
|
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -57,7 +56,7 @@ export default class IconButton extends Vue {
|
|||||||
}
|
}
|
||||||
&__text {
|
&__text {
|
||||||
display: none;
|
display: none;
|
||||||
font-family: $artistic-font;
|
font-family: $font-artistic;
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
color: $color-primary;
|
color: $color-primary;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|||||||
@@ -104,8 +104,7 @@ export default class MacOsInstructions extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
@import "@/presentation/styles/fonts.scss";
|
|
||||||
|
|
||||||
li {
|
li {
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
|
|||||||
@@ -150,7 +150,8 @@ function getDefaultCode(language: ScriptingLanguage): string {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
|
|
||||||
::v-deep .code-area {
|
::v-deep .code-area {
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ export default class MenuOptionList extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/fonts.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
|
|
||||||
$gap: 0.25rem;
|
$gap: 0.25rem;
|
||||||
.list {
|
.list {
|
||||||
font-family: $normal-font;
|
font-family: $font-normal;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
.items {
|
.items {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export default class MenuOptionListItem extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
|
|
||||||
.enabled {
|
.enabled {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default class Handle extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
|
|
||||||
$color : $color-primary-dark;
|
$color : $color-primary-dark;
|
||||||
$color-hover : $color-primary;
|
$color-hover : $color-primary;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default class HorizontalResizeSlider extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "@/presentation/styles/media.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
|
|
||||||
.slider {
|
.slider {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -53,7 +53,7 @@ export default class HorizontalResizeSlider extends Vue {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: var(--second-min-width);
|
min-width: var(--second-min-width);
|
||||||
}
|
}
|
||||||
@media screen and (max-width: $vertical-view-breakpoint) {
|
@media screen and (max-width: $media-vertical-view-breakpoint) {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
.first {
|
.first {
|
||||||
width: auto !important;
|
width: auto !important;
|
||||||
|
|||||||
@@ -105,13 +105,12 @@ function isClickable(element: Element) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/fonts.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
@import "@/presentation/styles/components/card.scss";
|
|
||||||
|
|
||||||
.cards {
|
.cards {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
font-family: $main-font;
|
font-family: $font-main;
|
||||||
gap: $card-gap;
|
gap: $card-gap;
|
||||||
/*
|
/*
|
||||||
Padding is used to allow scale animation (growing size) for cards on hover.
|
Padding is used to allow scale animation (growing size) for cards on hover.
|
||||||
@@ -124,6 +123,6 @@ function isClickable(element: Element) {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 3.5em;
|
font-size: 3.5em;
|
||||||
font-family: $normal-font;
|
font-family: $font-normal;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -95,9 +95,7 @@ export default class CardListItem extends StatefulVue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
@import "@/presentation/styles/media.scss";
|
|
||||||
@import "@/presentation/styles/components/card.scss";
|
|
||||||
|
|
||||||
$card-inner-padding : 30px;
|
$card-inner-padding : 30px;
|
||||||
$arrow-size : 15px;
|
$arrow-size : 15px;
|
||||||
|
|||||||
@@ -24,7 +24,8 @@
|
|||||||
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
|
|
||||||
.documentationUrls {
|
.documentationUrls {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
@@ -32,7 +32,8 @@
|
|||||||
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
|
|
||||||
#node {
|
#node {
|
||||||
display:flex;
|
display:flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
@@ -52,14 +52,14 @@ export default class RevertToggle extends StatefulVue {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@use 'sass:math';
|
@use 'sass:math';
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
|
|
||||||
$color-unchecked-bullet : $color-primary-darker;
|
$color-bullet-unchecked : $color-primary-darker;
|
||||||
$color-unchecked-text : $color-on-primary;
|
$color-bullet-checked : $color-on-secondary;
|
||||||
$color-unchecked-bg : $color-primary;
|
$color-text-unchecked : $color-on-primary;
|
||||||
$color-checked-bg : $color-secondary;
|
$color-text-checked : $color-on-secondary;
|
||||||
$color-checked-text : $color-on-secondary;
|
$color-bg-unchecked : $color-primary;
|
||||||
$color-checked-bullet : $color-on-secondary;
|
$color-bg-checked : $color-secondary;
|
||||||
$size-width : 85px;
|
$size-width : 85px;
|
||||||
$size-height : 30px;
|
$size-height : 30px;
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ $size-height : 30px;
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: $size-width;
|
width: $size-width;
|
||||||
height: $size-height;
|
height: $size-height;
|
||||||
background-color: $color-unchecked-bg;
|
background-color: $color-bg-unchecked;
|
||||||
-webkit-transition: background-color 0.25s ease-out 0s;
|
-webkit-transition: background-color 0.25s ease-out 0s;
|
||||||
transition: background-color 0.25s ease-out 0s;
|
transition: background-color 0.25s ease-out 0s;
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ $size-height : 30px;
|
|||||||
height: $circle-size;
|
height: $circle-size;
|
||||||
border-radius: $circle-size * 2;
|
border-radius: $circle-size * 2;
|
||||||
-webkit-border-radius: $circle-size * 2;
|
-webkit-border-radius: $circle-size * 2;
|
||||||
background-color: $color-unchecked-bullet;
|
background-color: $color-bullet-unchecked;
|
||||||
top: $size-height * 0.16;
|
top: $size-height * 0.16;
|
||||||
left: $size-width * 0.05;
|
left: $size-width * 0.05;
|
||||||
-webkit-transition: left 0.3s ease-out 0s;
|
-webkit-transition: left 0.3s ease-out 0s;
|
||||||
@@ -120,11 +120,11 @@ $size-height : 30px;
|
|||||||
|
|
||||||
input.input-checkbox:checked {
|
input.input-checkbox:checked {
|
||||||
+ .checkbox-animate {
|
+ .checkbox-animate {
|
||||||
background-color: $color-checked-bg;
|
background-color: $color-bg-checked;
|
||||||
}
|
}
|
||||||
+ .checkbox-animate:before {
|
+ .checkbox-animate:before {
|
||||||
left: ($size-width - math.div($size-width, 3.5));
|
left: ($size-width - math.div($size-width, 3.5));
|
||||||
background-color: $color-checked-bullet;
|
background-color: $color-bullet-checked;
|
||||||
}
|
}
|
||||||
+ .checkbox-animate .checkbox-off {
|
+ .checkbox-animate .checkbox-off {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -146,7 +146,7 @@ $size-height : 30px;
|
|||||||
.checkbox-off {
|
.checkbox-off {
|
||||||
margin-left: math.div($size-width, 3);
|
margin-left: math.div($size-width, 3);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
color: $color-unchecked-text;
|
color: $color-text-unchecked;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-on {
|
.checkbox-on {
|
||||||
@@ -154,7 +154,7 @@ $size-height : 30px;
|
|||||||
float: right;
|
float: right;
|
||||||
margin-right: math.div($size-width, 3);
|
margin-right: math.div($size-width, 3);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
color: $color-checked-text;
|
color: $color-text-checked;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -95,15 +95,13 @@ export default class TheScriptsView extends StatefulVue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
@import "@/presentation/styles/fonts.scss";
|
|
||||||
@import "@/presentation/styles/media.scss";
|
|
||||||
|
|
||||||
$inner-margin: 4px;
|
$margin-inner: 4px;
|
||||||
|
|
||||||
.scripts {
|
.scripts {
|
||||||
margin-top: $inner-margin;
|
margin-top: $margin-inner;
|
||||||
@media screen and (min-width: $vertical-view-breakpoint) { // so the current code is always visible
|
@media screen and (min-width: $media-vertical-view-breakpoint) { // so the current code is always visible
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
max-height: 70vh;
|
max-height: 70vh;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,12 +31,11 @@ export default class Dialog extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/fonts.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
@import "@/presentation/styles/colors.scss";
|
|
||||||
|
|
||||||
.dialog {
|
.dialog {
|
||||||
color: $color-surface;
|
color: $color-surface;
|
||||||
font-family: $normal-font;
|
font-family: $font-normal;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
@@ -38,8 +38,7 @@ export default class DownloadUrlList extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/media.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
@import "@/presentation/styles/colors.scss";
|
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
display:flex;
|
display:flex;
|
||||||
|
|||||||
@@ -56,11 +56,12 @@ export default class PrivacyPolicy extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/fonts.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
|
|
||||||
.privacy-policy {
|
.privacy-policy {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
font-family: $normal-font;
|
font-family: $font-normal;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
|
|
||||||
.line {
|
.line {
|
||||||
|
|||||||
@@ -81,9 +81,7 @@ export default class TheFooter extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
@import "@/presentation/styles/fonts.scss";
|
|
||||||
@import "@/presentation/styles/media.scss";
|
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
@@ -93,13 +91,13 @@ export default class TheFooter extends Vue {
|
|||||||
.footer {
|
.footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@media screen and (max-width: $big-screen-width) {
|
@media screen and (max-width: $media-screen-big-width) {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
&__section {
|
&__section {
|
||||||
display: flex;
|
display: flex;
|
||||||
@media screen and (max-width: $big-screen-width) {
|
@media screen and (max-width: $media-screen-big-width) {
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
width:100%;
|
width:100%;
|
||||||
&:not(:first-child) {
|
&:not(:first-child) {
|
||||||
@@ -108,13 +106,13 @@ export default class TheFooter extends Vue {
|
|||||||
}
|
}
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-family: $normal-font;
|
font-family: $font-normal;
|
||||||
&__item:not(:first-child) {
|
&__item:not(:first-child) {
|
||||||
&::before {
|
&::before {
|
||||||
content: "|";
|
content: "|";
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
}
|
}
|
||||||
@media screen and (max-width: $big-screen-width) {
|
@media screen and (max-width: $media-screen-big-width) {
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
&::before {
|
&::before {
|
||||||
content: "";
|
content: "";
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ export default class TheHeader extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
@import "@/presentation/styles/fonts.scss";
|
|
||||||
#container {
|
#container {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -37,7 +37,7 @@ export default class TheHeader extends Vue {
|
|||||||
.title {
|
.title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-family: $main-font;
|
font-family: $font-main;
|
||||||
font-size: 2.5em;
|
font-size: 2.5em;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ export default class TheHeader extends Vue {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
color: $color-primary;
|
color: $color-primary;
|
||||||
font-family: $artistic-font;
|
font-family: $font-artistic;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,8 +58,7 @@ export default class TheSearchBar extends StatefulVue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/colors.scss";
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
@import "@/presentation/styles/fonts.scss";
|
|
||||||
|
|
||||||
.search {
|
.search {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -81,7 +80,7 @@ export default class TheSearchBar extends StatefulVue {
|
|||||||
padding-right:10px;
|
padding-right:10px;
|
||||||
outline: none;
|
outline: none;
|
||||||
color: $color-primary;
|
color: $color-primary;
|
||||||
font-family: $normal-font;
|
font-family: $font-normal;
|
||||||
font-size:1em;
|
font-size:1em;
|
||||||
&:focus {
|
&:focus {
|
||||||
color: $color-primary-darker;
|
color: $color-primary-darker;
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
$big-screen-width: 992px;
|
|
||||||
$medium-screen-width: 768px;
|
|
||||||
$small-screen-width: 380px;
|
|
||||||
|
|
||||||
$vertical-view-breakpoint: 992px;
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
// Overrides base styling for LiquorTree
|
|
||||||
@import "@/presentation/styles/colors.scss";
|
|
||||||
|
|
||||||
$color-tree-bg : $color-primary-darker;
|
|
||||||
$color-node-arrow : $color-on-primary;
|
|
||||||
$color-node-fg : $color-on-primary;
|
|
||||||
$color-node-hover-bg : $color-primary-dark;
|
|
||||||
$color-node-keyboard-bg : $color-surface;
|
|
||||||
$color-node-keyboard-fg : $color-on-surface;
|
|
||||||
$color-node-checkbox-checked-bg : $color-secondary;
|
|
||||||
$color-node-checkbox-checked-border : $color-secondary;
|
|
||||||
$color-node-checkbox-checked-checked-tick : $color-on-secondary;
|
|
||||||
$color-node-checkbox-unchecked-bg : $color-primary-darkest;
|
|
||||||
$color-node-checkbox-unchecked-border : $color-on-primary;
|
|
||||||
|
|
||||||
.tree {
|
|
||||||
background: $color-tree-bg;
|
|
||||||
&-node {
|
|
||||||
white-space: normal !important;
|
|
||||||
> .tree-content {
|
|
||||||
> .tree-anchor > span {
|
|
||||||
color: $color-node-fg;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 1.5em;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
background: $color-node-hover-bg !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.selected { // When using keyboard navigation it highlights current item and its child items
|
|
||||||
background: $color-node-keyboard-bg;
|
|
||||||
.tree-text {
|
|
||||||
color: $color-node-keyboard-fg !important; // $block
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&-checkbox {
|
|
||||||
border-color: $color-node-checkbox-unchecked-border !important;
|
|
||||||
&.checked {
|
|
||||||
background: $color-node-checkbox-checked-bg !important;
|
|
||||||
border-color: $color-node-checkbox-checked-border !important;
|
|
||||||
&:after {
|
|
||||||
border-color: $color-node-checkbox-checked-checked-tick !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.indeterminate {
|
|
||||||
border-color: $color-node-checkbox-unchecked-border !important;
|
|
||||||
}
|
|
||||||
background: $color-node-checkbox-unchecked-bg !important;
|
|
||||||
}
|
|
||||||
&-arrow {
|
|
||||||
&.has-child {
|
|
||||||
&.rtl:after, &:after {
|
|
||||||
border-color: $color-node-arrow !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,265 +1,283 @@
|
|||||||
import 'mocha';
|
import 'mocha';
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { IScript } from '@/domain/IScript';
|
|
||||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||||
import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
|
import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
|
||||||
import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
|
import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
|
||||||
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
|
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
|
||||||
import { SelectedScriptStub } from '@tests/unit/stubs/SelectedScriptStub';
|
import { SelectedScriptStub } from '@tests/unit/stubs/SelectedScriptStub';
|
||||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||||
|
import { UserSelectionTestRunner } from './UserSelectionTestRunner';
|
||||||
|
|
||||||
describe('UserSelection', () => {
|
describe('UserSelection', () => {
|
||||||
describe('ctor', () => {
|
describe('ctor', () => {
|
||||||
it('has nothing with no initial selection', () => {
|
describe('has nothing with no initial selection', () => {
|
||||||
// arrange
|
// arrange
|
||||||
const collection = new CategoryCollectionStub().withAction(new CategoryStub(1).withScriptIds('s1'));
|
const allScripts = [
|
||||||
const selection = [];
|
new SelectedScriptStub('s1', false),
|
||||||
|
];
|
||||||
|
new UserSelectionTestRunner()
|
||||||
|
.withSelectedScripts([])
|
||||||
|
.withCategory(1, allScripts.map((s) => s.script))
|
||||||
// act
|
// act
|
||||||
const sut = new UserSelection(collection, selection);
|
.run()
|
||||||
// assert
|
// assert
|
||||||
expect(sut.selectedScripts).to.have.lengthOf(0);
|
.expectFinalScripts([]);
|
||||||
});
|
});
|
||||||
it('has initial selection', () => {
|
describe('has initial selection', () => {
|
||||||
// arrange
|
// arrange
|
||||||
const firstScript = new ScriptStub('1');
|
const scripts = [
|
||||||
const secondScript = new ScriptStub('2');
|
new SelectedScriptStub('s1', false),
|
||||||
const collection = new CategoryCollectionStub()
|
new SelectedScriptStub('s2', false),
|
||||||
.withAction(new CategoryStub(1).withScript(firstScript).withScripts(secondScript));
|
];
|
||||||
const expected = [ new SelectedScript(firstScript, false), new SelectedScript(secondScript, true) ];
|
new UserSelectionTestRunner()
|
||||||
|
.withSelectedScripts(scripts)
|
||||||
|
.withCategory(1, scripts.map((s) => s.script))
|
||||||
// act
|
// act
|
||||||
const sut = new UserSelection(collection, expected);
|
.run()
|
||||||
// assert
|
// assert
|
||||||
expect(sut.selectedScripts).to.deep.include(expected[0]);
|
.expectFinalScripts(scripts);
|
||||||
expect(sut.selectedScripts).to.deep.include(expected[1]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('deselectAll removes all items', () => {
|
describe('deselectAll removes all items', () => {
|
||||||
// arrange
|
// arrange
|
||||||
const events: Array<readonly SelectedScript[]> = [];
|
const allScripts = [
|
||||||
const collection = new CategoryCollectionStub()
|
new SelectedScriptStub('s1', false),
|
||||||
.withAction(new CategoryStub(1)
|
new SelectedScriptStub('s2', false),
|
||||||
.withScriptIds('s1', 's2', 's3', 's4'));
|
new SelectedScriptStub('s3', false),
|
||||||
const selectedScripts = [
|
new SelectedScriptStub('s4', false),
|
||||||
new SelectedScriptStub('s1'), new SelectedScriptStub('s2'), new SelectedScriptStub('s3'),
|
|
||||||
];
|
];
|
||||||
const sut = new UserSelection(collection, selectedScripts);
|
const selectedScripts = allScripts.filter(
|
||||||
sut.changed.on((newScripts) => events.push(newScripts));
|
(s) => ['s1', 's2', 's3'].includes(s.id));
|
||||||
|
new UserSelectionTestRunner()
|
||||||
|
.withSelectedScripts(selectedScripts)
|
||||||
|
.withCategory(1, allScripts.map((s) => s.script))
|
||||||
// act
|
// act
|
||||||
sut.deselectAll();
|
.run((sut) => {
|
||||||
|
sut.deselectAll();
|
||||||
|
})
|
||||||
// assert
|
// assert
|
||||||
expect(sut.selectedScripts).to.have.length(0);
|
.expectTotalFiredEvents(1)
|
||||||
expect(events).to.have.lengthOf(1);
|
.expectFinalScripts([])
|
||||||
expect(events[0]).to.have.length(0);
|
.expectFinalScriptsInEvent(0, []);
|
||||||
});
|
});
|
||||||
it('selectOnly selects expected', () => {
|
describe('selectOnly selects expected', () => {
|
||||||
// arrange
|
// arrange
|
||||||
const events: Array<readonly SelectedScript[]> = [];
|
const allScripts = [
|
||||||
const collection = new CategoryCollectionStub()
|
new SelectedScriptStub('s1', false),
|
||||||
.withAction(new CategoryStub(1)
|
new SelectedScriptStub('s2', false),
|
||||||
.withScriptIds('s1', 's2', 's3', 's4'));
|
new SelectedScriptStub('s3', false),
|
||||||
const selectedScripts = [
|
new SelectedScriptStub('s4', false),
|
||||||
new SelectedScriptStub('s1'), new SelectedScriptStub('s2'), new SelectedScriptStub('s3'),
|
|
||||||
];
|
];
|
||||||
const sut = new UserSelection(collection, selectedScripts);
|
const selectedScripts = allScripts.filter(
|
||||||
sut.changed.on((newScripts) => events.push(newScripts));
|
(s) => ['s1', 's2', 's3'].includes(s.id));
|
||||||
const scripts = [new ScriptStub('s2'), new ScriptStub('s3'), new ScriptStub('s4')];
|
const scriptsToSelect = allScripts.filter(
|
||||||
const expected = [ new SelectedScriptStub('s2'), new SelectedScriptStub('s3'),
|
(s) => ['s2', 's3', 's4'].includes(s.id));
|
||||||
new SelectedScript(scripts[2], false)];
|
new UserSelectionTestRunner()
|
||||||
|
.withSelectedScripts(selectedScripts)
|
||||||
|
.withCategory(1, allScripts.map((s) => s.script))
|
||||||
// act
|
// act
|
||||||
sut.selectOnly(scripts);
|
.run((sut) => {
|
||||||
|
sut.selectOnly(scriptsToSelect.map((s) => s.script));
|
||||||
|
})
|
||||||
// assert
|
// assert
|
||||||
expect(sut.selectedScripts).to.have.deep.members(expected,
|
.expectTotalFiredEvents(1)
|
||||||
`Expected: ${JSON.stringify(sut.selectedScripts)}\n` +
|
.expectFinalScripts(scriptsToSelect)
|
||||||
`Actual: ${JSON.stringify(expected)}`);
|
.expectFinalScriptsInEvent(0, scriptsToSelect);
|
||||||
expect(events).to.have.lengthOf(1);
|
|
||||||
expect(events[0]).to.deep.equal(expected);
|
|
||||||
});
|
});
|
||||||
it('selectAll selects as expected', () => {
|
describe('selectAll selects as expected', () => {
|
||||||
// arrange
|
// arrange
|
||||||
const events: Array<readonly SelectedScript[]> = [];
|
const expected = [
|
||||||
const scripts: IScript[] = [new ScriptStub('s1'), new ScriptStub('s2'), new ScriptStub('s3'), new ScriptStub('s4')];
|
new SelectedScriptStub('s1', false),
|
||||||
const collection = new CategoryCollectionStub()
|
new SelectedScriptStub('s2', false),
|
||||||
.withAction(new CategoryStub(1)
|
];
|
||||||
.withScripts(...scripts));
|
new UserSelectionTestRunner()
|
||||||
const sut = new UserSelection(collection, []);
|
.withSelectedScripts([])
|
||||||
sut.changed.on((newScripts) => events.push(newScripts));
|
.withCategory(1, expected.map((s) => s.script))
|
||||||
const expected = scripts.map((script) => new SelectedScript(script, false));
|
|
||||||
// act
|
// act
|
||||||
sut.selectAll();
|
.run((sut) => {
|
||||||
|
sut.selectAll();
|
||||||
|
})
|
||||||
// assert
|
// assert
|
||||||
expect(sut.selectedScripts).to.deep.equal(expected);
|
.expectTotalFiredEvents(1)
|
||||||
expect(events).to.have.lengthOf(1);
|
.expectFinalScripts(expected)
|
||||||
expect(events[0]).to.deep.equal(expected);
|
.expectFinalScriptsInEvent(0, expected);
|
||||||
});
|
});
|
||||||
describe('addOrUpdateSelectedScript', () => {
|
describe('addOrUpdateSelectedScript', () => {
|
||||||
it('adds when item does not exist', () => {
|
describe('adds when item does not exist', () => {
|
||||||
// arrange
|
// arrange
|
||||||
const events: Array<readonly SelectedScript[]> = [];
|
const scripts = [new ScriptStub('s1'), new ScriptStub('s2')];
|
||||||
const collection = new CategoryCollectionStub()
|
const expected = [ new SelectedScript(scripts[0], false) ];
|
||||||
.withAction(new CategoryStub(1)
|
new UserSelectionTestRunner()
|
||||||
.withScripts(new ScriptStub('s1'), new ScriptStub('s2')));
|
.withSelectedScripts([])
|
||||||
const sut = new UserSelection(collection, []);
|
.withCategory(1, scripts)
|
||||||
sut.changed.on((scripts) => events.push(scripts));
|
|
||||||
const expected = [ new SelectedScript(new ScriptStub('s1'), false) ];
|
|
||||||
// act
|
// act
|
||||||
sut.addOrUpdateSelectedScript('s1', false);
|
.run((sut) => {
|
||||||
|
sut.addOrUpdateSelectedScript(scripts[0].id, false);
|
||||||
|
})
|
||||||
// assert
|
// assert
|
||||||
expect(sut.selectedScripts).to.deep.equal(expected);
|
.expectTotalFiredEvents(1)
|
||||||
expect(events).to.have.lengthOf(1);
|
.expectFinalScripts(expected)
|
||||||
expect(events[0]).to.deep.equal(expected);
|
.expectFinalScriptsInEvent(0, expected);
|
||||||
});
|
});
|
||||||
it('updates when item exists', () => {
|
describe('updates when item exists', () => {
|
||||||
// arrange
|
// arrange
|
||||||
const events: Array<readonly SelectedScript[]> = [];
|
const scripts = [new ScriptStub('s1'), new ScriptStub('s2')];
|
||||||
const collection = new CategoryCollectionStub()
|
const existing = new SelectedScript(scripts[0], false);
|
||||||
.withAction(new CategoryStub(1)
|
const expected = new SelectedScript(scripts[0], true);
|
||||||
.withScripts(new ScriptStub('s1'), new ScriptStub('s2')));
|
new UserSelectionTestRunner()
|
||||||
const sut = new UserSelection(collection, []);
|
.withSelectedScripts([existing])
|
||||||
sut.changed.on((scripts) => events.push(scripts));
|
.withCategory(1, scripts)
|
||||||
const expected = [ new SelectedScript(new ScriptStub('s1'), true) ];
|
|
||||||
// act
|
// act
|
||||||
sut.addOrUpdateSelectedScript('s1', true);
|
.run((sut) => {
|
||||||
|
sut.addOrUpdateSelectedScript(expected.id, expected.revert);
|
||||||
|
})
|
||||||
// assert
|
// assert
|
||||||
expect(sut.selectedScripts).to.deep.equal(expected);
|
.expectTotalFiredEvents(1)
|
||||||
expect(events).to.have.lengthOf(1);
|
.expectFinalScripts([ expected ])
|
||||||
expect(events[0]).to.deep.equal(expected);
|
.expectFinalScriptsInEvent(0, [ expected ]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('removeAllInCategory', () => {
|
describe('removeAllInCategory', () => {
|
||||||
it('does nothing when nothing exists', () => {
|
describe('does nothing when nothing exists', () => {
|
||||||
// arrange
|
// arrange
|
||||||
const events: Array<readonly SelectedScript[]> = [];
|
const categoryId = 99;
|
||||||
const categoryId = 1;
|
const scripts = [new ScriptStub('s1'), new ScriptStub('s2')];
|
||||||
const collection = new CategoryCollectionStub()
|
new UserSelectionTestRunner()
|
||||||
.withAction(new CategoryStub(categoryId)
|
.withSelectedScripts([])
|
||||||
.withScripts(new ScriptStub('s1'), new ScriptStub('s2')));
|
.withCategory(categoryId, scripts)
|
||||||
const sut = new UserSelection(collection, []);
|
|
||||||
sut.changed.on((s) => events.push(s));
|
|
||||||
// act
|
// act
|
||||||
sut.removeAllInCategory(categoryId);
|
.run((sut) => {
|
||||||
|
sut.removeAllInCategory(categoryId);
|
||||||
|
})
|
||||||
// assert
|
// assert
|
||||||
expect(events).to.have.lengthOf(0);
|
.expectTotalFiredEvents(0)
|
||||||
expect(sut.selectedScripts).to.have.lengthOf(0);
|
.expectFinalScripts([]);
|
||||||
});
|
});
|
||||||
it('removes all when all exists', () => {
|
describe('removes all when all exists', () => {
|
||||||
// arrange
|
// arrange
|
||||||
const categoryId = 1;
|
const categoryId = 34;
|
||||||
const scripts = [new SelectedScriptStub('s1'), new SelectedScriptStub('s2')];
|
const scripts = [new SelectedScriptStub('s1'), new SelectedScriptStub('s2')];
|
||||||
const collection = new CategoryCollectionStub()
|
new UserSelectionTestRunner()
|
||||||
.withAction(new CategoryStub(categoryId)
|
.withSelectedScripts(scripts)
|
||||||
.withScripts(...scripts.map((script) => script.script)));
|
.withCategory(categoryId, scripts.map((s) => s.script))
|
||||||
const sut = new UserSelection(collection, scripts);
|
|
||||||
// act
|
// act
|
||||||
sut.removeAllInCategory(categoryId);
|
.run((sut) => {
|
||||||
|
sut.removeAllInCategory(categoryId);
|
||||||
|
})
|
||||||
// assert
|
// assert
|
||||||
expect(sut.selectedScripts.length).to.equal(0);
|
.expectTotalFiredEvents(1)
|
||||||
|
.expectFinalScripts([]);
|
||||||
});
|
});
|
||||||
it('removes existing some exists', () => {
|
describe('removes existing when some exists', () => {
|
||||||
// arrange
|
// arrange
|
||||||
const categoryId = 1;
|
const categoryId = 55;
|
||||||
const existing = [new ScriptStub('s1'), new ScriptStub('s2')];
|
const existing = [new ScriptStub('s1'), new ScriptStub('s2')];
|
||||||
const notExisting = [new ScriptStub('s3'), new ScriptStub('s4')];
|
const notExisting = [new ScriptStub('s3'), new ScriptStub('s4')];
|
||||||
const collection = new CategoryCollectionStub()
|
new UserSelectionTestRunner()
|
||||||
.withAction(new CategoryStub(categoryId)
|
.withSelectedScripts(existing.map((script) => new SelectedScript(script, false)))
|
||||||
.withScripts(...existing, ...notExisting));
|
.withCategory(categoryId, [ ...existing, ...notExisting ])
|
||||||
const sut = new UserSelection(collection, existing.map((script) => new SelectedScript(script, false)));
|
|
||||||
// act
|
// act
|
||||||
sut.removeAllInCategory(categoryId);
|
.run((sut) => {
|
||||||
|
sut.removeAllInCategory(categoryId);
|
||||||
|
})
|
||||||
// assert
|
// assert
|
||||||
expect(sut.selectedScripts.length).to.equal(0);
|
.expectTotalFiredEvents(1)
|
||||||
|
.expectFinalScripts([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('addOrUpdateAllInCategory', () => {
|
describe('addOrUpdateAllInCategory', () => {
|
||||||
it('does nothing when all already exists', () => {
|
describe('when all already exists', () => {
|
||||||
// arrange
|
describe('does nothing if nothing is changed', () => {
|
||||||
const events: Array<readonly SelectedScript[]> = [];
|
// arrange
|
||||||
const categoryId = 1;
|
const categoryId = 55;
|
||||||
const scripts = [new ScriptStub('s1'), new ScriptStub('s2')];
|
const existingScripts = [
|
||||||
const collection = new CategoryCollectionStub()
|
new SelectedScriptStub('s1', false),
|
||||||
.withAction(new CategoryStub(categoryId)
|
new SelectedScriptStub('s2', false),
|
||||||
.withScripts(...scripts));
|
];
|
||||||
const sut = new UserSelection(collection, scripts.map((script) => new SelectedScript(script, false)));
|
new UserSelectionTestRunner()
|
||||||
sut.changed.on((s) => events.push(s));
|
.withSelectedScripts(existingScripts)
|
||||||
// act
|
.withCategory(categoryId, existingScripts.map((s) => s.script))
|
||||||
sut.addOrUpdateAllInCategory(categoryId);
|
// act
|
||||||
// assert
|
.run((sut) => {
|
||||||
expect(events).to.have.lengthOf(0);
|
sut.addOrUpdateAllInCategory(categoryId);
|
||||||
expect(sut.selectedScripts.map((script) => script.id))
|
})
|
||||||
.to.have.deep.members(scripts.map((script) => script.id));
|
// assert
|
||||||
|
.expectTotalFiredEvents(0)
|
||||||
|
.expectFinalScripts(existingScripts);
|
||||||
|
});
|
||||||
|
describe('changes revert status of all', () => {
|
||||||
|
// arrange
|
||||||
|
const newStatus = false;
|
||||||
|
const scripts = [
|
||||||
|
new SelectedScriptStub('e1', !newStatus),
|
||||||
|
new SelectedScriptStub('e2', !newStatus),
|
||||||
|
new SelectedScriptStub('e3', newStatus),
|
||||||
|
];
|
||||||
|
const expectedScripts = scripts.map((s) => new SelectedScript(s.script, newStatus));
|
||||||
|
const categoryId = 31;
|
||||||
|
new UserSelectionTestRunner()
|
||||||
|
.withSelectedScripts(scripts)
|
||||||
|
.withCategory(categoryId, scripts.map((s) => s.script))
|
||||||
|
// act
|
||||||
|
.run((sut) => {
|
||||||
|
sut.addOrUpdateAllInCategory(categoryId, newStatus);
|
||||||
|
})
|
||||||
|
// assert
|
||||||
|
.expectTotalFiredEvents(1)
|
||||||
|
.expectFinalScripts(expectedScripts)
|
||||||
|
.expectFinalScriptsInEvent(0, expectedScripts);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
it('adds all when nothing exists', () => {
|
describe('when nothing exists; adds all with given revert status', () => {
|
||||||
// arrange
|
const revertStatuses = [ true, false ];
|
||||||
const categoryId = 1;
|
for (const revertStatus of revertStatuses) {
|
||||||
const expected = [new ScriptStub('s1'), new ScriptStub('s2')];
|
describe(`when revert status is ${revertStatus}`, () => {
|
||||||
const collection = new CategoryCollectionStub()
|
// arrange
|
||||||
.withAction(new CategoryStub(categoryId)
|
const categoryId = 1;
|
||||||
.withScripts(...expected));
|
const scripts = [
|
||||||
const sut = new UserSelection(collection, []);
|
new SelectedScriptStub('s1', !revertStatus),
|
||||||
// act
|
new SelectedScriptStub('s2', !revertStatus),
|
||||||
sut.addOrUpdateAllInCategory(categoryId);
|
];
|
||||||
// assert
|
const expected = scripts.map((s) => new SelectedScript(s.script, revertStatus));
|
||||||
expect(sut.selectedScripts.map((script) => script.id))
|
new UserSelectionTestRunner()
|
||||||
.to.have.deep.members(expected.map((script) => script.id));
|
.withSelectedScripts([])
|
||||||
|
.withCategory(categoryId, scripts.map((s) => s.script))
|
||||||
|
// act
|
||||||
|
.run((sut) => {
|
||||||
|
sut.addOrUpdateAllInCategory(categoryId, revertStatus);
|
||||||
|
})
|
||||||
|
// assert
|
||||||
|
.expectTotalFiredEvents(1)
|
||||||
|
.expectFinalScripts(expected)
|
||||||
|
.expectFinalScriptsInEvent(0, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
it('adds all with given revert status when nothing exists', () => {
|
describe('when some exists; changes revert status of all', () => {
|
||||||
// arrange
|
// arrange
|
||||||
const categoryId = 1;
|
const newStatus = true;
|
||||||
const expected = [new ScriptStub('s1'), new ScriptStub('s2')];
|
const existing = [
|
||||||
const collection = new CategoryCollectionStub()
|
new SelectedScriptStub('e1', true),
|
||||||
.withAction(new CategoryStub(categoryId)
|
new SelectedScriptStub('e2', false),
|
||||||
.withScripts(...expected));
|
];
|
||||||
const sut = new UserSelection(collection, []);
|
const notExisting = [
|
||||||
// act
|
new SelectedScriptStub('n3', true),
|
||||||
sut.addOrUpdateAllInCategory(categoryId, true);
|
new SelectedScriptStub('n4', false),
|
||||||
// assert
|
];
|
||||||
expect(sut.selectedScripts.every((script) => script.revert))
|
|
||||||
.to.equal(true);
|
|
||||||
});
|
|
||||||
it('changes revert status of all when some exists', () => {
|
|
||||||
// arrange
|
|
||||||
const categoryId = 1;
|
|
||||||
const notExisting = [ new ScriptStub('notExisting1'), new ScriptStub('notExisting2') ];
|
|
||||||
const existing = [ new ScriptStub('existing1'), new ScriptStub('existing2') ];
|
|
||||||
const allScripts = [ ...existing, ...notExisting ];
|
const allScripts = [ ...existing, ...notExisting ];
|
||||||
const collection = new CategoryCollectionStub()
|
const expectedScripts = allScripts.map((s) => new SelectedScript(s.script, newStatus));
|
||||||
.withAction(new CategoryStub(categoryId)
|
const categoryId = 77;
|
||||||
.withScripts(...allScripts));
|
new UserSelectionTestRunner()
|
||||||
const sut = new UserSelection(collection, existing.map((script) => new SelectedScript(script, false)));
|
.withSelectedScripts(existing)
|
||||||
|
.withCategory(categoryId, allScripts.map((s) => s.script))
|
||||||
// act
|
// act
|
||||||
sut.addOrUpdateAllInCategory(categoryId, true);
|
.run((sut) => {
|
||||||
|
sut.addOrUpdateAllInCategory(categoryId, newStatus);
|
||||||
|
})
|
||||||
// assert
|
// assert
|
||||||
expect(sut.selectedScripts.every((script) => script.revert))
|
.expectTotalFiredEvents(1)
|
||||||
.to.equal(true);
|
.expectFinalScripts(expectedScripts)
|
||||||
});
|
.expectFinalScriptsInEvent(0, expectedScripts);
|
||||||
it('changes revert status of all when some exists', () => {
|
|
||||||
// arrange
|
|
||||||
const categoryId = 1;
|
|
||||||
const notExisting = [ new ScriptStub('notExisting1'), new ScriptStub('notExisting2') ];
|
|
||||||
const existing = [ new ScriptStub('existing1'), new ScriptStub('existing2') ];
|
|
||||||
const allScripts = [ ...existing, ...notExisting ];
|
|
||||||
const collection = new CategoryCollectionStub()
|
|
||||||
.withAction(new CategoryStub(categoryId)
|
|
||||||
.withScripts(...allScripts));
|
|
||||||
const sut = new UserSelection(collection, existing.map((script) => new SelectedScript(script, false)));
|
|
||||||
// act
|
|
||||||
sut.addOrUpdateAllInCategory(categoryId, true);
|
|
||||||
// assert
|
|
||||||
expect(sut.selectedScripts.every((script) => script.revert))
|
|
||||||
.to.equal(true);
|
|
||||||
});
|
|
||||||
it('changes revert status of all when all already exists', () => {
|
|
||||||
// arrange
|
|
||||||
const categoryId = 1;
|
|
||||||
const scripts = [ new ScriptStub('existing1'), new ScriptStub('existing2') ];
|
|
||||||
const collection = new CategoryCollectionStub()
|
|
||||||
.withAction(new CategoryStub(categoryId)
|
|
||||||
.withScripts(...scripts));
|
|
||||||
const sut = new UserSelection(collection, scripts.map((script) => new SelectedScript(script, false)));
|
|
||||||
// act
|
|
||||||
sut.addOrUpdateAllInCategory(categoryId, true);
|
|
||||||
// assert
|
|
||||||
expect(sut.selectedScripts.every((script) => script.revert))
|
|
||||||
.to.equal(true);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('isSelected', () => {
|
describe('isSelected', () => {
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import { expect } from 'chai';
|
||||||
|
import 'mocha';
|
||||||
|
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||||
|
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
|
||||||
|
import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
|
||||||
|
import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
|
||||||
|
import { IScript } from '@/domain/IScript';
|
||||||
|
|
||||||
|
export class UserSelectionTestRunner {
|
||||||
|
private readonly collection = new CategoryCollectionStub();
|
||||||
|
private existingScripts: readonly SelectedScript[] = [];
|
||||||
|
private events: Array<readonly SelectedScript[]> = [];
|
||||||
|
private sut: UserSelection;
|
||||||
|
|
||||||
|
public withCategory(categoryId: number, scripts: readonly IScript[]) {
|
||||||
|
const category = new CategoryStub(categoryId)
|
||||||
|
.withScripts(...scripts);
|
||||||
|
this.collection
|
||||||
|
.withAction(category);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public withSelectedScripts(existingScripts: readonly SelectedScript[]) {
|
||||||
|
this.existingScripts = existingScripts;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public run(runner?: (sut: UserSelection) => void) {
|
||||||
|
this.sut = this.createSut();
|
||||||
|
if (runner) {
|
||||||
|
runner(this.sut);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public expectTotalFiredEvents(amount: number) {
|
||||||
|
const testName = amount === 0 ? 'does not fire changed event' : `fires changed event ${amount} times`;
|
||||||
|
it(testName, () => {
|
||||||
|
expect(this.events).to.have.lengthOf(amount);
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public expectFinalScripts(finalScripts: readonly SelectedScript[]) {
|
||||||
|
expectSameScripts(finalScripts, this.sut.selectedScripts);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public expectFinalScriptsInEvent(eventIndex: number, finalScripts: readonly SelectedScript[]) {
|
||||||
|
expectSameScripts(this.events[eventIndex], finalScripts);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
private createSut(): UserSelection {
|
||||||
|
const sut = new UserSelection(this.collection, this.existingScripts);
|
||||||
|
sut.changed.on((s) => this.events.push(s));
|
||||||
|
return sut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectSameScripts(actual: readonly SelectedScript[], expected: readonly SelectedScript[]) {
|
||||||
|
it('has same expected scripts', () => {
|
||||||
|
const existingScriptIds = expected.map((script) => script.id).sort();
|
||||||
|
const expectedScriptIds = actual.map((script) => script.id).sort();
|
||||||
|
expect(existingScriptIds).to.deep.equal(expectedScriptIds);
|
||||||
|
});
|
||||||
|
it('has expected revert state', () => {
|
||||||
|
const scriptsWithDifferentStatus = actual
|
||||||
|
.filter((script) => {
|
||||||
|
const other = expected.find((existing) => existing.id === script.id);
|
||||||
|
if (!other) {
|
||||||
|
throw new Error(`Script "${script.id}" does not exist in expected scripts: ${JSON.stringify(expected, null, '\t')}`);
|
||||||
|
}
|
||||||
|
return script.revert !== other.revert;
|
||||||
|
});
|
||||||
|
expect(scriptsWithDifferentStatus).to.have
|
||||||
|
.lengthOf(0, 'Scripts with different statuses:\n' + scriptsWithDifferentStatus
|
||||||
|
.map((s) =>
|
||||||
|
`[id: ${s.id}, actual status: ${s.revert}, ` +
|
||||||
|
`expected status: ${expected.find((existing) => existing.id === s.id).revert}]`)
|
||||||
|
.join(' , '),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import 'mocha';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import WindowsData from 'raw-loader!@/application/collections/windows.yaml';
|
||||||
|
import MacOsData from 'raw-loader!@/application/collections/macos.yaml';
|
||||||
|
|
||||||
|
/*
|
||||||
|
A common mistake when working with yaml files to forget mentioning that a value should
|
||||||
|
be interpreted as multi-line string using "|".
|
||||||
|
E.g.
|
||||||
|
```
|
||||||
|
code: |-
|
||||||
|
echo Hello
|
||||||
|
echo World
|
||||||
|
```
|
||||||
|
If "|" is missing then the code is inlined like `echo Hello echo World``, which can be
|
||||||
|
unintended. This test checks for similar issues in collection yaml files.
|
||||||
|
These tests can be considered as "linter" more than "unit-test" and therefore can lead
|
||||||
|
to false-positives.
|
||||||
|
*/
|
||||||
|
describe('collection files to have no unintended inlining', async () => {
|
||||||
|
// arrange
|
||||||
|
const testCases = [ {
|
||||||
|
name: 'macos',
|
||||||
|
fileContent: MacOsData,
|
||||||
|
}, {
|
||||||
|
name: 'windows',
|
||||||
|
fileContent: WindowsData,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
for (const testCase of testCases) {
|
||||||
|
it(`${testCase.name}`, async () => {
|
||||||
|
const lines = await findBadLineNumbers(testCase.fileContent);
|
||||||
|
expect(lines).to.be.have.lengthOf(0,
|
||||||
|
`Did you intend to have multi-lined string in lines: `
|
||||||
|
+ lines.map(((line) => line.toString())).join(', '),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function findBadLineNumbers(fileContent: string): Promise<number[]> {
|
||||||
|
return [
|
||||||
|
...findLineNumbersEndingWith(fileContent, 'revertCode:'),
|
||||||
|
...findLineNumbersEndingWith(fileContent, 'code:'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function findLineNumbersEndingWith(content: string, ending: string): number[] {
|
||||||
|
sanityCheck(content, ending);
|
||||||
|
const lines = content.split(/\r\n|\r|\n/);
|
||||||
|
const results = new Array<number>();
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
if (line.trim().endsWith(ending)) {
|
||||||
|
results.push((i + 1 /* first line is 1 not 0 */));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function sanityCheck(content: string, ending: string): void {
|
||||||
|
if (!content.includes(ending)) {
|
||||||
|
throw new Error(
|
||||||
|
`File does not contain string "${ending}" string at all.`
|
||||||
|
+ `Did the word "${ending}" change? Or is this sanity check wrong?`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
4
tests/unit/application/collections/raw-loader.d.ts
vendored
Normal file
4
tests/unit/application/collections/raw-loader.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
declare module 'raw-loader!@/*' {
|
||||||
|
const contents: string;
|
||||||
|
export default contents;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user