Add Windows save instructions UI and fix URL #296
- Add Windows instruction dialog when saving scripts for Windows. - Fix incorrect macOS download URL given for Linux instructions. - Refactor UI rendering, eleminating the use of `v-html` and JavaScript variables to hold HTML code.
This commit is contained in:
@@ -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<IInstructionsBuilderData>();
|
||||
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`,
|
||||
};
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
@@ -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));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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';
|
||||
@@ -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<SupportedOperatingSystem, Component> = {
|
||||
[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<typeof useCollectionState>;
|
||||
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,
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user