diff --git a/README.md b/README.md
index 01c0fd73..1ee7289a 100644
--- a/README.md
+++ b/README.md
@@ -137,7 +137,7 @@ For a detailed comparison of features between the desktop and web versions of pr
- **Transparent**. Have full visibility into what the tweaks do as you enable them.
- **Reversible**. Revert if something feels wrong.
- **Accessible**. No need to run any compiled software on your computer with web version.
-- **Secure**: Security is a top priority at privacy.sexy with [comprehensive safeguards](./SECURITY.md#application-security) in place.
+- **Secure**: Security is a top priority at privacy.sexy with [comprehensive safeguards](./SECURITY.md#security-practices) in place.
- **Open**. What you see as code in this repository is what you get. The application itself, its infrastructure and deployments are open-source and automated thanks to [bump-everywhere](https://github.com/undergroundwires/bump-everywhere).
- **Tested**. A lot of tests. Automated and manual. Community-testing and verification. Stability improvements comes before new features.
- **Extensible**. Effortlessly [extend scripts](./CONTRIBUTING.md#extend-scripts) with a custom designed [templating language](./docs/templating.md).
diff --git a/src/presentation/components/Code/CodeButtons/Save/CodeSaveButton.vue b/src/presentation/components/Code/CodeButtons/Save/CodeSaveButton.vue
index 4850c37a..7a73cd5d 100644
--- a/src/presentation/components/Code/CodeButtons/Save/CodeSaveButton.vue
+++ b/src/presentation/components/Code/CodeButtons/Save/CodeSaveButton.vue
@@ -5,8 +5,8 @@
:icon-name="isRunningAsDesktopApplication ? 'floppy-disk' : 'file-arrow-down'"
@click="saveCode"
/>
-
-
+
+
@@ -22,14 +22,12 @@ import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
import { ScriptFilename } from '@/application/CodeRunner/ScriptFilename';
import { Dialog, FileType } from '@/presentation/common/Dialog';
import IconButton from '../IconButton.vue';
-import InstructionList from './Instructions/InstructionList.vue';
-import { IInstructionListData } from './Instructions/InstructionListData';
-import { getInstructions } from './Instructions/InstructionListDataFactory';
+import RunInstructions from './RunInstructions/RunInstructions.vue';
export default defineComponent({
components: {
IconButton,
- InstructionList,
+ RunInstructions,
ModalDialog,
},
setup() {
@@ -39,10 +37,6 @@ export default defineComponent({
const areInstructionsVisible = ref(false);
const filename = computed(() => buildFilename(currentState.value.collection.scripting));
- const instructions = computed(() => getInstructions(
- currentState.value.collection.os,
- filename.value,
- ));
async function saveCode() {
const { success, error } = await dialog.saveFile(
@@ -59,8 +53,8 @@ export default defineComponent({
return {
isRunningAsDesktopApplication,
- instructions,
areInstructionsVisible,
+ filename,
saveCode,
};
},
diff --git a/src/presentation/components/Code/CodeButtons/Save/Instructions/Data/InstructionsBuilder.ts b/src/presentation/components/Code/CodeButtons/Save/Instructions/Data/InstructionsBuilder.ts
deleted file mode 100644
index 0284a443..00000000
--- a/src/presentation/components/Code/CodeButtons/Save/Instructions/Data/InstructionsBuilder.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { OperatingSystem } from '@/domain/OperatingSystem';
-import { IInstructionListData, IInstructionListStep } from '../InstructionListData';
-
-export interface IInstructionsBuilderData {
- readonly fileName: string;
-}
-
-export type InstructionStepBuilderType = (data: IInstructionsBuilderData) => IInstructionListStep;
-
-export class InstructionsBuilder {
- private readonly stepBuilders = new Array();
-
- constructor(private readonly os: OperatingSystem) {
-
- }
-
- public withStep(stepBuilder: InstructionStepBuilderType) {
- this.stepBuilders.push(stepBuilder);
- return this;
- }
-
- public build(data: IInstructionsBuilderData): IInstructionListData {
- return {
- operatingSystem: this.os,
- steps: this.stepBuilders.map((stepBuilder) => stepBuilder(data)),
- };
- }
-}
diff --git a/src/presentation/components/Code/CodeButtons/Save/Instructions/Data/LinuxInstructionsBuilder.ts b/src/presentation/components/Code/CodeButtons/Save/Instructions/Data/LinuxInstructionsBuilder.ts
deleted file mode 100644
index ce280c05..00000000
--- a/src/presentation/components/Code/CodeButtons/Save/Instructions/Data/LinuxInstructionsBuilder.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import { OperatingSystem } from '@/domain/OperatingSystem';
-import { InstructionsBuilder } from './InstructionsBuilder';
-
-export class LinuxInstructionsBuilder extends InstructionsBuilder {
- constructor() {
- super(OperatingSystem.Linux);
- super
- .withStep(() => ({
- action: {
- instruction: 'Download the file.',
- details: 'You should have already been prompted to save the script file.'
- + '
If this was not the case or you did not save the script when prompted,'
- + '
please try to download your script file again.',
- },
- }))
- .withStep(() => ({
- action: {
- instruction: 'Open terminal.',
- details:
- 'Opening terminal changes based on the distro you run.'
- + '
You may search for "Terminal" in your application launcher to find it.'
- + '
'
- + '
Alternatively use terminal shortcut for your distro if it has one by default:'
- + ''
- + 'Ctrl-Alt-T: Ubuntu, CentOS, Linux Mint, Elementary OS, ubermix, Kali… '
- + 'Super-T: Pop!_OS… '
- + 'Alt-T: Parrot OS… '
- + 'Ctrl-Alt-Insert: Bodhi Linux… '
- + '
'
- ,
- },
- }))
- .withStep(() => ({
- action: {
- instruction: 'Navigate to the folder where you downloaded the file e.g.:',
- },
- code: {
- instruction: 'cd ~/Downloads',
- details: 'Press on enter/return key after running the command.'
- + '
If the file is not downloaded on Downloads folder,'
- + '
change Downloads to path where the file is downloaded.'
- + '
'
- + '
This command means:'
- + ''
- + 'cd will change the current folder. '
- + '~ is the user home directory. '
- + '
',
- },
- }))
- .withStep((data) => ({
- action: {
- instruction: 'Give the file execute permissions:',
- },
- code: {
- instruction: `chmod +x ${data.fileName}`,
- details: 'Press on enter/return key after running the command.
'
- + 'It will make the file executable.
'
- + 'If you use desktop environment you can alternatively (instead of running the command):'
- + ''
- + '- Locate the file using your file manager.
'
- + '- Right click on the file, select "Properties".
'
- + '- Go to "Permissions" and check "Allow executing file as program".
'
- + '
'
- + '
These GUI steps and name of options may change depending on your file manager.'
- ,
- },
- }))
- .withStep((data) => ({
- action: {
- instruction: 'Execute the file:',
- },
- code: {
- instruction: `./${data.fileName}`,
- details:
- 'If you have desktop environment, instead of running this command you can alternatively:'
- + ''
- + '- Locate the file using your file manager.
'
- + '- Right click on the file, select "Run as program".
'
- + '
'
- ,
- },
- }))
- .withStep(() => ({
- action: {
- instruction: 'If asked, enter your administrator password.',
- details: 'As you type, your password will be hidden but the keys are still registered, so keep typing.'
- + '
Press on enter/return key after typing your password.'
- + '
Administrator privileges are required to configure OS.',
- },
- }));
- }
-}
diff --git a/src/presentation/components/Code/CodeButtons/Save/Instructions/Data/MacOsInstructionsBuilder.ts b/src/presentation/components/Code/CodeButtons/Save/Instructions/Data/MacOsInstructionsBuilder.ts
deleted file mode 100644
index b3a60860..00000000
--- a/src/presentation/components/Code/CodeButtons/Save/Instructions/Data/MacOsInstructionsBuilder.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-import { OperatingSystem } from '@/domain/OperatingSystem';
-import { InstructionsBuilder } from './InstructionsBuilder';
-
-export class MacOsInstructionsBuilder extends InstructionsBuilder {
- constructor() {
- super(OperatingSystem.macOS);
- super
- .withStep(() => ({
- action: {
- instruction: 'Download the file.',
- details: 'You should have already been prompted to save the script file.'
- + '
If this was not the case or you did not save the script when prompted,'
- + '
please try to download your script file again.'
- ,
- },
- }))
- .withStep(() => ({
- action: {
- instruction: 'Open terminal.',
- details: 'Type Terminal into Spotlight or open it from the Applications -> Utilities folder.',
- },
- }))
- .withStep(() => ({
- action: {
- instruction: 'Navigate to the folder where you downloaded the file e.g.:',
- },
- code: {
- instruction: 'cd ~/Downloads',
- details: 'Press on enter/return key after running the command.'
- + '
If the file is not downloaded on Downloads folder,'
- + '
change Downloads to path where the file is downloaded.'
- + '
'
- + '
This command means:'
- + ''
- + 'cd will change the current folder. '
- + '~ is the user home directory. '
- + '
',
- },
- }))
- .withStep((data) => ({
- action: {
- instruction: 'Give the file execute permissions:',
- },
- code: {
- instruction: `chmod +x ${data.fileName}`,
- details: 'Press on enter/return key after running the command.
'
- + 'It will make the file executable.'
- ,
- },
- }))
- .withStep((data) => ({
- action: {
- instruction: 'Execute the file:',
- },
- code: {
- instruction: `./${data.fileName}`,
- details: 'Alternatively you can locate the file in Finder and double click on it.',
- },
- }))
- .withStep(() => ({
- action: {
- instruction: 'If asked, enter your administrator password.',
- details: 'As you type, your password will be hidden but the keys are still registered, so keep typing.'
- + '
Press on enter/return key after typing your password.'
- + '
Administrator privileges are required to configure OS.'
- ,
- },
- }));
- }
-}
diff --git a/src/presentation/components/Code/CodeButtons/Save/Instructions/InfoTooltip.vue b/src/presentation/components/Code/CodeButtons/Save/Instructions/InfoTooltip.vue
deleted file mode 100644
index 710157bb..00000000
--- a/src/presentation/components/Code/CodeButtons/Save/Instructions/InfoTooltip.vue
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/src/presentation/components/Code/CodeButtons/Save/Instructions/InstructionList.vue b/src/presentation/components/Code/CodeButtons/Save/Instructions/InstructionList.vue
deleted file mode 100644
index c0078211..00000000
--- a/src/presentation/components/Code/CodeButtons/Save/Instructions/InstructionList.vue
+++ /dev/null
@@ -1,119 +0,0 @@
-
-
-
- You have two alternatives to apply your selection.
-
-
-
- 1. The easy alternative. Run your script without any manual steps by
- downloading desktop version of {{ appName }} on the
- {{ osName }} system you wish to configure, and then click on the Run button. This is
- recommended for most users.
-
-
-
- 2. The hard (manual) alternative. This requires you to do additional manual
- steps. If you are unsure how to follow the instructions, tap or hover on information
- (Engage with icons like this for extra wisdom!)
- icons near the steps, or follow the easy alternative described above.
-
-
-
- -
-
-
{{ step.action.instruction }}
-
-
-
-
{{ step.code.instruction }}
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/presentation/components/Code/CodeButtons/Save/Instructions/InstructionListData.ts b/src/presentation/components/Code/CodeButtons/Save/Instructions/InstructionListData.ts
deleted file mode 100644
index ee314548..00000000
--- a/src/presentation/components/Code/CodeButtons/Save/Instructions/InstructionListData.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { OperatingSystem } from '@/domain/OperatingSystem';
-
-export interface IInstructionListData {
- readonly operatingSystem: OperatingSystem;
- readonly steps: readonly IInstructionListStep[];
-}
-
-export interface IInstructionListStep {
- readonly action: IInstructionInfo;
- readonly code?: IInstructionInfo;
-}
-
-export interface IInstructionInfo {
- readonly instruction: string;
- readonly details?: string;
-}
diff --git a/src/presentation/components/Code/CodeButtons/Save/Instructions/InstructionListDataFactory.ts b/src/presentation/components/Code/CodeButtons/Save/Instructions/InstructionListDataFactory.ts
deleted file mode 100644
index 6d2deccb..00000000
--- a/src/presentation/components/Code/CodeButtons/Save/Instructions/InstructionListDataFactory.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { OperatingSystem } from '@/domain/OperatingSystem';
-import { InstructionsBuilder } from './Data/InstructionsBuilder';
-import { MacOsInstructionsBuilder } from './Data/MacOsInstructionsBuilder';
-import { IInstructionListData } from './InstructionListData';
-import { LinuxInstructionsBuilder } from './Data/LinuxInstructionsBuilder';
-
-const builders = new Map([
- [OperatingSystem.macOS, new MacOsInstructionsBuilder()],
- [OperatingSystem.Linux, new LinuxInstructionsBuilder()],
-]);
-
-export function getInstructions(
- os: OperatingSystem,
- fileName: string,
-): IInstructionListData | undefined {
- return builders
- .get(os)
- ?.build({ fileName });
-}
diff --git a/src/presentation/components/Code/CodeButtons/Save/RunInstructions/InfoTooltip.vue b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/InfoTooltip.vue
new file mode 100644
index 00000000..4c35a5fb
--- /dev/null
+++ b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/InfoTooltip.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/src/presentation/components/Code/CodeButtons/Save/RunInstructions/RunInstructions.vue b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/RunInstructions.vue
new file mode 100644
index 00000000..670f2525
--- /dev/null
+++ b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/RunInstructions.vue
@@ -0,0 +1,85 @@
+
+
+
+ You have two alternatives to apply your selection.
+
+
+
+ 1. The easy alternative. Run your script without any manual steps by
+ downloading desktop version of {{ appName }} on the
+ {{ osName }} system you wish to configure, and then click on the Run button. This is
+ recommended for most users.
+
+
+
+ 2. The hard (manual) alternative. This requires you to do additional manual
+ steps. If you are unsure how to follow the instructions, tap or hover on information
+ Engage with icons like this for extra wisdom!
+ icons near the steps, or follow the easy alternative described above.
+
+
+
+
+
+
+
+
+
+
diff --git a/src/presentation/components/Code/CodeButtons/Save/Instructions/CodeInstruction.vue b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/CopyableCommand.vue
similarity index 90%
rename from src/presentation/components/Code/CodeButtons/Save/Instructions/CodeInstruction.vue
rename to src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/CopyableCommand.vue
index cb505a6c..9377b35b 100644
--- a/src/presentation/components/Code/CodeButtons/Save/Instructions/CodeInstruction.vue
+++ b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/CopyableCommand.vue
@@ -27,10 +27,10 @@ export default defineComponent({
setup() {
const { copyText } = injectKey((keys) => keys.useClipboard);
- const codeElement = shallowRef();
+ const codeElementRef = shallowRef();
async function copyCode() {
- const element = codeElement.value;
+ const element = codeElementRef.value;
if (!element) {
throw new Error('Code element could not be found.');
}
@@ -43,7 +43,7 @@ export default defineComponent({
return {
copyCode,
- codeElement,
+ codeElement: codeElementRef,
};
},
});
@@ -53,7 +53,7 @@ export default defineComponent({
@use "@/presentation/assets/styles/main" as *;
.code-wrapper {
- display:flex;
+ display: inline-flex;
white-space: nowrap;
justify-content: space-between;
font-family: $font-normal;
diff --git a/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/InstructionStep.vue b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/InstructionStep.vue
new file mode 100644
index 00000000..b1ec0c59
--- /dev/null
+++ b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/InstructionStep.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/InstructionSteps.vue b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/InstructionSteps.vue
new file mode 100644
index 00000000..d44dbe33
--- /dev/null
+++ b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/InstructionSteps.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/PlatformInstructionSteps.vue b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/PlatformInstructionSteps.vue
new file mode 100644
index 00000000..5e99fc3a
--- /dev/null
+++ b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/PlatformInstructionSteps.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
diff --git a/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/Platforms/LinuxInstructions.vue b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/Platforms/LinuxInstructions.vue
new file mode 100644
index 00000000..a8f88eea
--- /dev/null
+++ b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/Platforms/LinuxInstructions.vue
@@ -0,0 +1,155 @@
+
+
+
+ Download the file.
+
+
+ You should have already been prompted to save the script file.
+
+
+ If this was not the case or you did not save the script when prompted,
+ please try to download your script file again.
+
+
+
+
+ Open terminal.
+
+
+ Opening terminal changes based on the distro you run.
+
+
+ You may search for "Terminal" in your application launcher to find it.
+
+
+ Alternatively use terminal shortcut for your distro if it has one by default:
+
+ -
+
Ctrl-Alt-T:
+ Ubuntu, CentOS, Linux Mint, Elementary OS, ubermix, Kali…
+
+ -
+
Super-T: Pop!_OS…
+
+ -
+
Alt-T: Parrot OS…
+
+ -
+
Ctrl-Alt-Insert: Bodhi Linux…
+
+
+
+
+
+
+
+ Navigate to the folder where you downloaded the file e.g.:
+
+
+
+ cd ~/Downloads
+
+
+
+ Press on enter/return key after running the command.
+
+
+ If the file is not downloaded on Downloads folder,
+ change Downloads to path where the file is downloaded.
+
+
+ This command means:
+
+ cd will change the current folder.
+ ~ is the user home directory.
+
+
+
+
+
+
+
+ Give the file execute permissions:
+
+
+
+ chmod +x {{ filename }}
+
+
+
+ Press on enter/return key after running the command.
+
+
+ It will make the file executable.
+
+
+ If you use desktop environment you can alternatively (instead of running the command):
+
+ - Locate the file using your file manager.
+ - Right click on the file, select "Properties".
+ - Go to "Permissions" and check "Allow executing file as program".
+
+
+
+ These GUI steps and name of options may change depending on your file manager.'
+
+
+
+
+
+
+ Execute the file:
+
+
+
+ ./{{ filename }}
+
+
+ If you have desktop environment, instead of running this command you can alternatively:
+
+ - Locate the file using your file manager.
+ - Right click on the file, select "Run as program".
+
+
+
+
+
+ If asked, enter your administrator password.
+
+
+ As you type, your password will be hidden but the keys are still
+ registered, so keep typing.
+
+
+ Press on enter/return key after typing your password.
+
+
+ Administrator privileges are required to configure OS.
+
+
+
+
+
+
+
diff --git a/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/Platforms/MacOsInstructions.vue b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/Platforms/MacOsInstructions.vue
new file mode 100644
index 00000000..9e4fcebb
--- /dev/null
+++ b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/Platforms/MacOsInstructions.vue
@@ -0,0 +1,117 @@
+
+
+
+ Download the file.
+
+
+ You should have already been prompted to save the script file.
+
+
+ If this was not the case or you did not save the script when prompted,
+ please try to download your script file again.
+
+
+
+
+ Open terminal.
+
+ Type Terminal into Spotlight or open it from the Applications -> Utilities folder.
+
+
+
+
+ Navigate to the folder where you downloaded the file e.g.:
+
+
+
+ cd ~/Downloads
+
+
+
+ Press on enter/return key after running the command.
+
+
+ If the file is not downloaded on Downloads folder,
+ change Downloads to path where the file is downloaded.
+
+
+ This command means:
+
+ cd will change the current folder.
+ ~ is the user home directory.
+
+
+
+
+
+
+
+ Give the file execute permissions:
+
+
+
+ chmod +x {{ filename }}
+
+
+
+ Press on enter/return key after running the command.
+
+
+ It will make the file executable.
+
+
+
+
+
+
+ Execute the file:
+
+
+
+ ./{{ filename }}
+
+
+ Alternatively you can locate the file in Finder and double click on it.
+
+
+
+
+ If asked, enter your administrator password.
+
+
+ As you type, your password will be hidden but the keys are
+ still registered, so keep typing.
+
+
+ Press on enter/return key after typing your password.
+
+
+ Administrator privileges are required to configure OS.
+
+
+
+
+
+
+
diff --git a/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/Platforms/WindowsInstructions.vue b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/Platforms/WindowsInstructions.vue
new file mode 100644
index 00000000..9297cbb7
--- /dev/null
+++ b/src/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/Platforms/WindowsInstructions.vue
@@ -0,0 +1,154 @@
+
+
+
+ Download the file.
+
+ If a save prompt doesn't appear, try downloading the script again.
+
+
+
+ If warned by your browser, keep the file.
+
+
+
+ Browsers may warn you when downloading scripts.
+
+
+ privacy.sexy scripts are verified to be safe and are dedicated to securing your privacy.
+
+
+ In Edge:
+
+ - Select Keep from the downloads section.
+ - Click Show more on the next warning.
+ - Select Keep anyway.
+
+
+
+ For Firefox and Chrome, typically no additional
+ action is needed.
+
+
+
+
+ If your antivirus (e.g., Defender) alerts you, address the warning.
+
+
+
+ Depending on the script, antivirus software may incorrectly flag the download as a threat.
+
+
+ These false positives are common for scripts that modify system settings.
+
+
+ To handle false warnings in Microsoft Defender:
+
+ - Open Virus & threat protection from the Start menu.
+ -
+ Locate the event in Protection history
+ that pertains to the script.
+
+ - In the event details, select Actions > Allow.
+ - If the script was deleted, please re-download it.
+
+
+
+ Caution: For your security, remember to:
+
+ - Only allow scripts from trusted sources.
+ - Avoid broad exclusions in your antivirus settings.
+ - Keep real-time protection enabled whenever possible.
+
+
+
+
+
+
+ Open the downloaded file.
+
+
+ Confirm any browser prompts to open the file.
+
+
+ This standard security measure ensures that you are aware of the script execution.
+
+
+ Firefox users, click OK to acknowledge the
+ executable file warning.
+
+
+ Edge and Chrome users usually will not
+ encounter additional prompts.
+
+
+
+
+ If prompted, confirm SmartScreen warnings.
+
+
+ Windows SmartScreen might display a cautionary message.
+
+
+ This happens since privacy.sexy scripts are not recognized
+ by Microsoft's certification process.
+
+
+
+ - Select More info.
+ - Select Run anyway.
+
+
+
+
+
+ If administrative permissions are requested, grant them.
+
+
+ The script may request administrative rights to apply changes.
+
+
+ This is necessary for the script to apply the intended privacy settings.
+
+
+ Click Yes to authorize and run the script.
+
+
+
+
+
+
+
diff --git a/tests/unit/presentation/components/Code/CodeButtons/Save/Instructions/Data/InstructionsBuilder.spec.ts b/tests/unit/presentation/components/Code/CodeButtons/Save/Instructions/Data/InstructionsBuilder.spec.ts
deleted file mode 100644
index 936682ee..00000000
--- a/tests/unit/presentation/components/Code/CodeButtons/Save/Instructions/Data/InstructionsBuilder.spec.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import { describe, it, expect } from 'vitest';
-import { OperatingSystem } from '@/domain/OperatingSystem';
-import { IInstructionsBuilderData, InstructionsBuilder, InstructionStepBuilderType } from '@/presentation/components/Code/CodeButtons/Save/Instructions/Data/InstructionsBuilder';
-import { IInstructionInfo, IInstructionListStep } from '@/presentation/components/Code/CodeButtons/Save/Instructions/InstructionListData';
-
-describe('InstructionsBuilder', () => {
- describe('withStep', () => {
- it('returns itself', () => {
- // arrange
- const expected = new InstructionsBuilder(OperatingSystem.Android);
- const step = () => createMockStep();
- // act
- const actual = expected.withStep(step);
- // assert
- expect(actual).to.equal(expected);
- });
- });
- describe('build', () => {
- it('builds with given data', () => {
- // arrange
- const expectedData = createMockData();
- const actualData = Array();
- const builder = new InstructionsBuilder(OperatingSystem.Android);
- const steps: readonly InstructionStepBuilderType[] = [createMockStep(), createMockStep()]
- .map((step) => (data) => {
- actualData.push(data);
- return step;
- });
- for (const step of steps) {
- builder.withStep(step);
- }
- // act
- builder.build(expectedData);
- // assert
- expect(actualData.every((data) => data === expectedData));
- });
- it('builds with every step', () => {
- // arrange
- const expectedSteps = [
- createMockStep('first'),
- createMockStep('second'),
- createMockStep('third'),
- ];
- const builder = new InstructionsBuilder(OperatingSystem.Android);
- const steps: readonly InstructionStepBuilderType[] = expectedSteps.map((step) => () => step);
- for (const step of steps) {
- builder.withStep(step);
- }
- // act
- const data = builder.build(createMockData());
- // assert
- const actualSteps = data.steps;
- expect(actualSteps).to.have.members(expectedSteps);
- });
- it('builds with expected OS', () => {
- // arrange
- const expected = OperatingSystem.Linux;
- const sut = new InstructionsBuilder(expected);
- // act
- const actual = sut.build(createMockData()).operatingSystem;
- // assert
- expect(true);
- expect(actual).to.equal(expected);
- });
- });
-});
-
-function createMockData(): IInstructionsBuilderData {
- return {
- fileName: 'instructions-file',
- };
-}
-
-function createMockStep(identifier = 'mock step'): IInstructionListStep {
- return {
- action: createMockInfo(`${identifier} | action`),
- code: createMockInfo(`${identifier} | code`),
- };
-}
-
-function createMockInfo(identifier = 'mock info'): IInstructionInfo {
- return {
- instruction: `${identifier} | mock instruction`,
- details: `${identifier} | mock details`,
- };
-}
diff --git a/tests/unit/presentation/components/Code/CodeButtons/Save/Instructions/Data/MacOsInstructionsBuilder.spec.ts b/tests/unit/presentation/components/Code/CodeButtons/Save/Instructions/Data/MacOsInstructionsBuilder.spec.ts
deleted file mode 100644
index 0893688c..00000000
--- a/tests/unit/presentation/components/Code/CodeButtons/Save/Instructions/Data/MacOsInstructionsBuilder.spec.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { describe } from 'vitest';
-import { OperatingSystem } from '@/domain/OperatingSystem';
-import { MacOsInstructionsBuilder } from '@/presentation/components/Code/CodeButtons/Save/Instructions/Data/MacOsInstructionsBuilder';
-import { runOsSpecificInstructionBuilderTests } from './OsSpecificInstructionBuilderTestRunner';
-
-describe('MacOsInstructionsBuilder', () => {
- runOsSpecificInstructionBuilderTests({
- factory: () => new MacOsInstructionsBuilder(),
- os: OperatingSystem.macOS,
- });
-});
diff --git a/tests/unit/presentation/components/Code/CodeButtons/Save/Instructions/Data/OsSpecificInstructionBuilderTestRunner.ts b/tests/unit/presentation/components/Code/CodeButtons/Save/Instructions/Data/OsSpecificInstructionBuilderTestRunner.ts
deleted file mode 100644
index 5bb8db11..00000000
--- a/tests/unit/presentation/components/Code/CodeButtons/Save/Instructions/Data/OsSpecificInstructionBuilderTestRunner.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { it, expect } from 'vitest';
-import { OperatingSystem } from '@/domain/OperatingSystem';
-import { InstructionsBuilder } from '@/presentation/components/Code/CodeButtons/Save/Instructions/Data/InstructionsBuilder';
-
-interface ITestData {
- readonly factory: () => InstructionsBuilder;
- readonly os: OperatingSystem;
-}
-
-export function runOsSpecificInstructionBuilderTests(data: ITestData) {
- it('builds multiple steps', () => {
- // arrange
- const sut = data.factory();
- // act
- const result = sut.build({ fileName: 'test.file' });
- // assert
- expect(result.steps).to.have.length.greaterThan(0);
- });
- it(`operatingSystem return ${OperatingSystem[data.os]}`, () => {
- // arrange
- const expected = data.os;
- const sut = data.factory();
- // act
- const result = sut.build({ fileName: 'test.file' });
- // assert
- expect(result.operatingSystem).to.equal(expected);
- });
-}
diff --git a/tests/unit/presentation/components/Code/CodeButtons/Save/Instructions/InstructionListDataFactory.spec.ts b/tests/unit/presentation/components/Code/CodeButtons/Save/Instructions/InstructionListDataFactory.spec.ts
deleted file mode 100644
index 01eb7bdb..00000000
--- a/tests/unit/presentation/components/Code/CodeButtons/Save/Instructions/InstructionListDataFactory.spec.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { describe, it, expect } from 'vitest';
-import { OperatingSystem } from '@/domain/OperatingSystem';
-import { getInstructions } from '@/presentation/components/Code/CodeButtons/Save/Instructions/InstructionListDataFactory';
-import { getEnumValues } from '@/application/Common/Enum';
-import { InstructionsBuilder } from '@/presentation/components/Code/CodeButtons/Save/Instructions/Data/InstructionsBuilder';
-import { AllSupportedOperatingSystems } from '@tests/shared/TestCases/SupportedOperatingSystems';
-
-describe('InstructionListDataFactory', () => {
- describe('getInstructions', () => {
- it('returns expected if os is supported', () => {
- // arrange
- const fileName = 'test.file';
- // act
- const actualResults = AllSupportedOperatingSystems.map((os) => getInstructions(os, fileName));
- // assert
- expect(actualResults.every((result) => result instanceof InstructionsBuilder));
- });
- it('return undefined if OS is not supported', () => {
- // arrange
- const expected = undefined;
- const fileName = 'test.file';
- const unsupportedOses = getEnumValues(OperatingSystem)
- .filter((value) => !AllSupportedOperatingSystems.includes(value));
- // act
- const actualResults = unsupportedOses.map((os) => getInstructions(os, fileName));
- // assert
- expect(actualResults.every((result) => result === expected));
- });
- });
-});
diff --git a/tests/unit/presentation/components/Code/CodeButtons/Save/Instructions/CodeInstruction.spec.ts b/tests/unit/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/CopyableCommand.spec.ts
similarity index 98%
rename from tests/unit/presentation/components/Code/CodeButtons/Save/Instructions/CodeInstruction.spec.ts
rename to tests/unit/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/CopyableCommand.spec.ts
index 05cfff69..47cbe16c 100644
--- a/tests/unit/presentation/components/Code/CodeButtons/Save/Instructions/CodeInstruction.spec.ts
+++ b/tests/unit/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/CopyableCommand.spec.ts
@@ -1,6 +1,6 @@
import { describe, it, expect } from 'vitest';
import { shallowMount } from '@vue/test-utils';
-import CodeInstruction from '@/presentation/components/Code/CodeButtons/Save/Instructions/CodeInstruction.vue';
+import CodeInstruction from '@/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/CopyableCommand.vue';
import { expectThrowsAsync } from '@tests/shared/Assertions/ExpectThrowsAsync';
import { InjectionKeys } from '@/presentation/injectionSymbols';
import { Clipboard } from '@/presentation/components/Shared/Hooks/Clipboard/Clipboard';
diff --git a/tests/unit/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/PlatformInstructionSteps.spec.ts b/tests/unit/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/PlatformInstructionSteps.spec.ts
new file mode 100644
index 00000000..b5ff27d9
--- /dev/null
+++ b/tests/unit/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/PlatformInstructionSteps.spec.ts
@@ -0,0 +1,70 @@
+import { shallowMount } from '@vue/test-utils';
+import PlatformInstructionSteps from '@/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/PlatformInstructionSteps.vue';
+import { useCollectionState } from '@/presentation/components/Shared/Hooks/UseCollectionState';
+import { InjectionKeys } from '@/presentation/injectionSymbols';
+import { UseCollectionStateStub } from '@tests/unit/shared/Stubs/UseCollectionStateStub';
+import { AllSupportedOperatingSystems, SupportedOperatingSystem } from '@tests/shared/TestCases/SupportedOperatingSystems';
+import { OperatingSystem } from '@/domain/OperatingSystem';
+import { CategoryCollectionStateStub } from '@tests/unit/shared/Stubs/CategoryCollectionStateStub';
+import WindowsInstructions from '@/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/Platforms/WindowsInstructions.vue';
+import MacOsInstructions from '@/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/Platforms/MacOsInstructions.vue';
+import LinuxInstructions from '@/presentation/components/Code/CodeButtons/Save/RunInstructions/Steps/Platforms/LinuxInstructions.vue';
+import type { Component } from 'vue';
+
+describe('PlatformInstructionSteps', () => {
+ const testScenarios: Record = {
+ [OperatingSystem.Windows]: WindowsInstructions,
+ [OperatingSystem.macOS]: MacOsInstructions,
+ [OperatingSystem.Linux]: LinuxInstructions,
+ };
+ AllSupportedOperatingSystems.forEach((operatingSystem) => {
+ it(`renders the correct component for ${OperatingSystem[operatingSystem]}`, () => {
+ // arrange
+ const expectedComponent = testScenarios[operatingSystem];
+ const useCollectionStateStub = new UseCollectionStateStub()
+ .withState(new CategoryCollectionStateStub().withOs(operatingSystem));
+
+ // act
+ const wrapper = mountComponent({
+ useCollectionState: useCollectionStateStub.get(),
+ });
+
+ // assert
+ expect(wrapper.findComponent(expectedComponent).exists()).to.equal(true);
+ });
+ it(`binds the correct filename for ${OperatingSystem[operatingSystem]}`, () => {
+ // arrange
+ const expectedFilename = 'expected-file-name.bat';
+ const wrappedComponent = testScenarios[operatingSystem];
+ const useCollectionStateStub = new UseCollectionStateStub()
+ .withState(new CategoryCollectionStateStub().withOs(operatingSystem));
+
+ // act
+ const wrapper = mountComponent({
+ useCollectionState: useCollectionStateStub.get(),
+ filename: expectedFilename,
+ });
+
+ // assert
+ const componentWrapper = wrapper.findComponent(wrappedComponent);
+ expect(componentWrapper.props('filename')).to.equal(expectedFilename);
+ });
+ });
+});
+
+function mountComponent(options?: {
+ readonly useCollectionState?: ReturnType;
+ readonly filename?: string;
+}) {
+ return shallowMount(PlatformInstructionSteps, {
+ global: {
+ provide: {
+ [InjectionKeys.useCollectionState.key]:
+ () => options?.useCollectionState ?? new UseCollectionStateStub().get(),
+ },
+ },
+ props: {
+ filename: options?.filename === undefined ? 'privacy-test-script.bat' : options.filename,
+ },
+ });
+}