refactor to allow switching ICategoryCollection context #40

This commit is contained in:
undergroundwires
2021-01-05 22:28:38 +01:00
parent 3455a2ca6c
commit 2e40605d59
32 changed files with 897 additions and 232 deletions

View File

@@ -0,0 +1,272 @@
import 'mocha';
import { expect } from 'chai';
import { ApplicationContext } from '@/application/Context/ApplicationContext';
import { OperatingSystem } from '@/domain/OperatingSystem';
import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
import { IApplicationContext, IApplicationContextChangedEvent } from '@/application/Context/IApplicationContext';
import { IApplication } from '@/domain/IApplication';
import { ApplicationStub } from '../../stubs/ApplicationStub';
import { CategoryCollectionStub } from '../../stubs/CategoryCollectionStub';
describe('ApplicationContext', () => {
describe('changeContext', () => {
describe('when initial os is changed to different one', () => {
it('collection is changed as expected', () => {
// arrange
const testContext = new ObservableApplicationContextFactory()
.withAppContainingCollections(OperatingSystem.Windows, OperatingSystem.macOS);
const expectedCollection = testContext.app.getCollection(OperatingSystem.macOS);
// act
const sut = testContext
.withInitialOs(OperatingSystem.Windows)
.construct();
sut.changeContext(OperatingSystem.macOS);
// assert
expect(sut.collection).to.equal(expectedCollection);
});
it('currentOs is changed as expected', () => {
// arrange
const expectedOs = OperatingSystem.macOS;
const testContext = new ObservableApplicationContextFactory()
.withAppContainingCollections(OperatingSystem.Windows, expectedOs);
// act
const sut = testContext
.withInitialOs(OperatingSystem.Windows)
.construct();
sut.changeContext(expectedOs);
// assert
expect(sut.currentOs).to.equal(expectedOs);
});
it('state is changed as expected', () => {
// arrange
const testContext = new ObservableApplicationContextFactory()
.withAppContainingCollections(OperatingSystem.Windows, OperatingSystem.macOS);
// act
const sut = testContext
.withInitialOs(OperatingSystem.Windows)
.construct();
sut.changeContext(OperatingSystem.macOS);
// assert
expectEmptyState(sut.state);
});
});
it('remembers old state when changed backed to same os', () => {
// arrange
const os = OperatingSystem.Windows;
const changedOs = OperatingSystem.macOS;
const testContext = new ObservableApplicationContextFactory()
.withAppContainingCollections(os, changedOs);
const expectedFilter = 'first-state';
// act
const sut = testContext
.withInitialOs(os)
.construct();
const firstState = sut.state;
firstState.filter.setFilter(expectedFilter);
sut.changeContext(os);
sut.changeContext(changedOs);
sut.state.filter.setFilter('second-state');
sut.changeContext(os);
// assert
const actualFilter = sut.state.filter.currentFilter.query;
expect(actualFilter).to.equal(expectedFilter);
});
describe('contextChanged', () => {
it('fired as expected on change', () => {
// arrange
const nextOs = OperatingSystem.macOS;
const testContext = new ObservableApplicationContextFactory()
.withAppContainingCollections(OperatingSystem.Windows, nextOs);
const expectedCollection = testContext.app.getCollection(OperatingSystem.macOS);
// act
const sut = testContext
.withInitialOs(OperatingSystem.Windows)
.construct();
sut.changeContext(nextOs);
// assert
expect(testContext.firedEvents.length).to.equal(1);
expect(testContext.firedEvents[0].newCollection).to.equal(expectedCollection);
expect(testContext.firedEvents[0].newState).to.equal(sut.state);
expect(testContext.firedEvents[0].newOs).to.equal(nextOs);
});
it('is not fired when initial os is changed to same one', () => {
// arrange
const os = OperatingSystem.Windows;
const testContext = new ObservableApplicationContextFactory()
.withAppContainingCollections(os);
// act
const sut = testContext
.withInitialOs(os)
.construct();
const initialState = sut.state;
initialState.filter.setFilter('dirty-state');
sut.changeContext(os);
// assert
expect(testContext.firedEvents.length).to.equal(0);
});
it('new event is fired for each change', () => {
// arrange
const os = OperatingSystem.Windows;
const changedOs = OperatingSystem.macOS;
const testContext = new ObservableApplicationContextFactory()
.withAppContainingCollections(os, changedOs);
// act
const sut = testContext
.withInitialOs(os)
.construct();
sut.changeContext(changedOs);
sut.changeContext(os);
sut.changeContext(changedOs);
// assert
const duplicates = getDuplicates(testContext.firedEvents);
expect(duplicates.length).to.be.equal(0);
});
});
});
describe('ctor', () => {
describe('app', () => {
it('throw when app is undefined', () => {
// arrange
const expectedError = 'undefined app';
const app = undefined;
const os = OperatingSystem.Windows;
// act
const act = () => new ApplicationContext(app, os);
// assert
expect(act).to.throw(expectedError);
});
});
describe('collection', () => {
it('returns right collection for expected OS', () => {
// arrange
const os = OperatingSystem.Windows;
const testContext = new ObservableApplicationContextFactory()
.withAppContainingCollections(os);
const expected = testContext.app.getCollection(os);
// act
const sut = testContext
.withInitialOs(os)
.construct();
// assert
const actual = sut.collection;
expect(actual).to.deep.equal(expected);
});
});
describe('state', () => {
it('initially returns an empty state', () => {
// arrange
const sut = new ObservableApplicationContextFactory()
.construct();
// act
const actual = sut.state;
// assert
expectEmptyState(actual);
});
});
describe('os', () => {
it('set as initial OS', () => {
// arrange
const expected = OperatingSystem.Windows;
const testContext = new ObservableApplicationContextFactory()
.withAppContainingCollections(OperatingSystem.macOS, expected);
// act
const sut = testContext
.withInitialOs(expected)
.construct();
// assert
const actual = sut.currentOs;
expect(actual).to.deep.equal(expected);
});
describe('throws when OS is invalid', () => {
// arrange
const testCases = [
{
name: 'out of range',
expectedError: 'os "9999" is out of range',
os: 9999,
},
{
name: 'undefined',
expectedError: 'undefined os',
os: undefined,
},
{
name: 'unknown',
expectedError: 'unknown os',
os: OperatingSystem.Unknown,
},
{
name: 'does not exist in application',
expectedError: 'os "Android" is not defined in application',
os: OperatingSystem.Android,
},
];
// act
for (const testCase of testCases) {
it(testCase.name, () => {
const act = () =>
new ObservableApplicationContextFactory()
.withInitialOs(testCase.os)
.construct();
// assert
expect(act).to.throw(testCase.expectedError);
});
}
});
});
describe('app', () => {
it('sets app as expected', () => {
// arrange
const os = OperatingSystem.Windows;
const expected = new ApplicationStub().withCollection(
new CategoryCollectionStub().withOs(os),
);
// act
const sut = new ObservableApplicationContextFactory()
.withApp(expected)
.withInitialOs(os)
.construct();
// assert
expect(expected).to.equal(sut.app);
});
});
});
});
class ObservableApplicationContextFactory {
private static DefaultOs = OperatingSystem.Windows;
public app: IApplication;
public firedEvents = new Array<IApplicationContextChangedEvent>();
private initialOs = ObservableApplicationContextFactory.DefaultOs;
constructor() {
this.withAppContainingCollections(ObservableApplicationContextFactory.DefaultOs);
}
public withAppContainingCollections(...oses: OperatingSystem[]): ObservableApplicationContextFactory {
const collectionValues = oses.map((os) => new CategoryCollectionStub().withOs(os));
const app = new ApplicationStub().withCollections(...collectionValues);
return this.withApp(app);
}
public withApp(app: IApplication): ObservableApplicationContextFactory {
this.app = app;
return this;
}
public withInitialOs(initialOs: OperatingSystem) {
this.initialOs = initialOs;
return this;
}
public construct()
: IApplicationContext {
const sut = new ApplicationContext(this.app, this.initialOs);
sut.contextChanged.on((newContext) => this.firedEvents.push(newContext));
return sut;
}
}
function getDuplicates<T>(list: readonly T[]): T[] {
return list.filter((item, index) => list.indexOf(item) !== index);
}
function expectEmptyState(state: ICategoryCollectionState) {
expect(!state.code.current);
expect(!state.filter.currentFilter);
expect(!state.selection);
}

View File

@@ -0,0 +1,54 @@
import 'mocha';
import { expect } from 'chai';
import { OperatingSystem } from '@/domain/OperatingSystem';
import { ICategoryCollection } from '@/domain/ICategoryCollection';
import { ApplicationParserType, buildContext } from '@/application/Context/ApplicationContextProvider';
import { CategoryCollectionStub } from '../../stubs/CategoryCollectionStub';
import { EnvironmentStub } from '../../stubs/EnvironmentStub';
import { ApplicationStub } from '../../stubs/ApplicationStub';
describe('ApplicationContextProvider', () => {
describe('buildContext', () => {
it('sets application from parser', () => {
// arrange
const expected = new ApplicationStub().withCollection(
new CategoryCollectionStub().withOs(OperatingSystem.macOS));
const parserMock: ApplicationParserType = () => expected;
// act
const context = buildContext(parserMock);
// assert
// TODO: expect(expected).to.equal(context.app);
});
describe('sets initial OS as expected', () => {
it('returns currentOs if it is supported', () => {
// arrange
const expected = OperatingSystem.Windows;
const environment = new EnvironmentStub().withOs(expected);
const parser = mockParser(new CategoryCollectionStub().withOs(expected));
// act
const context = buildContext(parser, environment);
// assert
expect(expected).to.equal(context.currentOs);
});
it('fallbacks to other os if OS in environment is not supported', () => {
// arrange
const expected = OperatingSystem.Windows;
const currentOs = OperatingSystem.macOS;
const environment = new EnvironmentStub().withOs(currentOs);
const parser = mockParser(new CategoryCollectionStub().withOs(expected));
// act
const context = buildContext(parser, environment);
// assert
const actual = context.currentOs;
expect(expected).to.equal(actual);
});
it('fallbacks to most supported os if current os is not supported', () => {
// TODO: After more than single collection can be parsed
});
});
});
});
function mockParser(result: ICategoryCollection): ApplicationParserType {
return () => new ApplicationStub().withCollection(result);
}

View File

@@ -14,8 +14,6 @@ import { CategoryStub } from '../../../../stubs/CategoryStub';
import { ScriptStub } from '../../../../stubs/ScriptStub';
import { CategoryCollectionStub } from '../../../../stubs/CategoryCollectionStub';
// TODO: Test scriptingDefinition: IScriptingDefinition logic
describe('ApplicationCode', () => {
describe('ctor', () => {
it('empty when selection is empty', () => {

View File

@@ -0,0 +1,108 @@
import 'mocha';
import { expect } from 'chai';
import { parseProjectInformation } from '@/application/Parser/ProjectInformationParser';
import { CategoryCollectionParserType, parseApplication } from '@/application/Parser/ApplicationParser';
import applicationFile, { YamlApplication } from 'js-yaml-loader!@/application/application.yaml';
import { IProjectInformation } from '@/domain/IProjectInformation';
import { ProjectInformation } from '@/domain/ProjectInformation';
import { ICategoryCollection } from '@/domain/ICategoryCollection';
import { OperatingSystem } from '@/domain/OperatingSystem';
import { CategoryCollectionStub } from '../../stubs/CategoryCollectionStub';
import { getProcessEnvironmentStub } from '../../stubs/ProcessEnvironmentStub';
import { YamlApplicationStub } from '../../stubs/YamlApplicationStub';
describe('ApplicationParser', () => {
describe('parseApplication', () => {
it('can parse current application', () => { // Integration test
// act
const act = () => parseApplication();
// assert
expect(act).to.not.throw();
});
describe('parser', () => {
it('returns result from the parser', () => {
// arrange
const os = OperatingSystem.macOS;
const expected = new CategoryCollectionStub()
.withOs(os);
const parser = new CategoryCollectionParserSpy()
.setResult(expected)
.mockParser();
// act
const context = parseApplication(parser);
// assert
const actual = context.getCollection(os);
expect(expected).to.equal(actual);
});
});
describe('processEnv', () => {
it('used to parse expected project information', () => {
// arrange
const env = getProcessEnvironmentStub();
const expected = parseProjectInformation(env);
const parserSpy = new CategoryCollectionParserSpy();
const parserMock = parserSpy.mockParser();
// act
const context = parseApplication(parserMock, env);
// assert
expect(expected).to.deep.equal(context.info);
expect(expected).to.deep.equal(parserSpy.lastArguments.info);
});
it('defaults to process.env', () => {
// arrange
const env = process.env;
const expected = parseProjectInformation(env);
const parserSpy = new CategoryCollectionParserSpy();
const parserMock = parserSpy.mockParser();
// act
const context = parseApplication(parserMock);
// assert
expect(expected).to.deep.equal(context.info);
expect(expected).to.deep.equal(parserSpy.lastArguments.info);
});
});
describe('collectionData', () => {
it('parsed with expected data', () => {
// arrange
const expected = new YamlApplicationStub();
const env = getProcessEnvironmentStub();
const parserSpy = new CategoryCollectionParserSpy();
const parserMock = parserSpy.mockParser();
// act
parseApplication(parserMock, env, expected);
// assert
expect(expected).to.equal(parserSpy.lastArguments.file);
});
it('defaults to applicationFile', () => {
// arrange
const expected = applicationFile;
const parserSpy = new CategoryCollectionParserSpy();
const parserMock = parserSpy.mockParser();
// act
parseApplication(parserMock);
// assert
expect(expected).to.equal(parserSpy.lastArguments.file);
});
});
});
});
class CategoryCollectionParserSpy {
public lastArguments: {
file: YamlApplication;
info: ProjectInformation;
} = { file: undefined, info: undefined };
private result: ICategoryCollection = new CategoryCollectionStub();
public setResult(collection: ICategoryCollection): CategoryCollectionParserSpy {
this.result = collection;
return this;
}
public mockParser(): CategoryCollectionParserType {
return (file: YamlApplication, info: IProjectInformation) => {
this.lastArguments.file = file;
this.lastArguments.info = info;
return this.result;
};
}
}

View File

@@ -1,30 +1,24 @@
import { IEntity } from '@/infrastructure/Entity/IEntity';
import applicationFile, { YamlCategory, YamlScript, YamlApplication, YamlScriptingDefinition } from 'js-yaml-loader!@/application/application.yaml';
import { parseCategoryCollection } from '@/application/Parser/CategoryCollectionParser';
import 'mocha';
import { expect } from 'chai';
import { IEntity } from '@/infrastructure/Entity/IEntity';
import { parseCategoryCollection } from '@/application/Parser/CategoryCollectionParser';
import { parseCategory } from '@/application/Parser/CategoryParser';
import { RecommendationLevel } from '@/domain/RecommendationLevel';
import { ScriptCompilerStub } from '../../stubs/ScriptCompilerStub';
import { parseProjectInformation } from '@/application/Parser/ProjectInformationParser';
import { OperatingSystem } from '@/domain/OperatingSystem';
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
import { parseScriptingDefinition } from '@/application/Parser/ScriptingDefinitionParser';
import { mockEnumParser } from '../../stubs/EnumParserStub';
import { ProjectInformationStub } from '../../stubs/ProjectInformationStub';
import { ScriptCompilerStub } from '../../stubs/ScriptCompilerStub';
import { getCategoryStub, YamlApplicationStub } from '../../stubs/YamlApplicationStub';
describe('CategoryCollectionParser', () => {
describe('parseCategoryCollection', () => {
it('can parse current application file', () => {
// act
const act = () => parseCategoryCollection(applicationFile);
// assert
expect(act).to.not.throw();
});
it('throws when undefined', () => {
// arrange
const expectedError = 'content is null or undefined';
const info = new ProjectInformationStub();
// act
const act = () => parseCategoryCollection(undefined);
const act = () => parseCategoryCollection(undefined, info);
// assert
expect(act).to.throw(expectedError);
});
@@ -32,29 +26,35 @@ describe('CategoryCollectionParser', () => {
it('throws when undefined actions', () => {
// arrange
const expectedError = 'content does not define any action';
const collection = new YamlApplicationBuilder().withActions(undefined).build();
const collection = new YamlApplicationStub()
.withActions(undefined);
const info = new ProjectInformationStub();
// act
const act = () => parseCategoryCollection(collection);
const act = () => parseCategoryCollection(collection, info);
// assert
expect(act).to.throw(expectedError);
});
it('throws when has no actions', () => {
// arrange
const expectedError = 'content does not define any action';
const collection = new YamlApplicationBuilder().withActions([]).build();
const collection = new YamlApplicationStub()
.withActions([]);
const info = new ProjectInformationStub();
// act
const act = () => parseCategoryCollection(collection);
const act = () => parseCategoryCollection(collection, info);
// assert
expect(act).to.throw(expectedError);
});
it('parses actions', () => {
// arrange
const actions = [ getTestCategory('test1'), getTestCategory('test2') ];
const actions = [ getCategoryStub('test1'), getCategoryStub('test2') ];
const compiler = new ScriptCompilerStub();
const expected = [ parseCategory(actions[0], compiler), parseCategory(actions[1], compiler) ];
const collection = new YamlApplicationBuilder().withActions(actions).build();
const collection = new YamlApplicationStub()
.withActions(actions);
const info = new ProjectInformationStub();
// act
const actual = parseCategoryCollection(collection).actions;
const actual = parseCategoryCollection(collection, info).actions;
// assert
expect(excludingId(actual)).to.be.deep.equal(excludingId(expected));
function excludingId<TId>(array: ReadonlyArray<IEntity<TId>>) {
@@ -65,60 +65,14 @@ describe('CategoryCollectionParser', () => {
}
});
});
describe('info', () => {
it('returns expected repository version', () => {
// arrange
const expected = 'expected-version';
const env = getProcessEnvironmentStub();
env.VUE_APP_VERSION = expected;
const collection = new YamlApplicationBuilder().build();
// act
const actual = parseCategoryCollection(collection, env).info.version;
// assert
expect(actual).to.be.equal(expected);
});
it('returns expected repository url', () => {
// arrange
const expected = 'https://expected-repository.url';
const env = getProcessEnvironmentStub();
env.VUE_APP_REPOSITORY_URL = expected;
const collection = new YamlApplicationBuilder().build();
// act
const actual = parseCategoryCollection(collection, env).info.repositoryUrl;
// assert
expect(actual).to.be.equal(expected);
});
it('returns expected name', () => {
// arrange
const expected = 'expected-app-name';
const env = getProcessEnvironmentStub();
env.VUE_APP_NAME = expected;
const collection = new YamlApplicationBuilder().build();
// act
const actual = parseCategoryCollection(collection, env).info.name;
// assert
expect(actual).to.be.equal(expected);
});
it('returns expected homepage url', () => {
// arrange
const expected = 'https://expected.sexy';
const env = getProcessEnvironmentStub();
env.VUE_APP_HOMEPAGE_URL = expected;
const collection = new YamlApplicationBuilder().build();
// act
const actual = parseCategoryCollection(collection, env).info.homepage;
// assert
expect(actual).to.be.equal(expected);
});
});
describe('scripting definition', () => {
it('parses scripting definition as expected', () => {
// arrange
const collection = new YamlApplicationBuilder().build();
const collection = new YamlApplicationStub();
const information = parseProjectInformation(process.env);
const expected = parseScriptingDefinition(collection.scripting, information);
// act
const actual = parseCategoryCollection(collection).scripting;
const actual = parseCategoryCollection(collection, information).scripting;
// assert
expect(expected).to.deep.equal(actual);
});
@@ -129,79 +83,15 @@ describe('CategoryCollectionParser', () => {
const expectedOs = OperatingSystem.macOS;
const osText = 'macos';
const expectedName = 'os';
const collection = new YamlApplicationBuilder()
.withOs(osText)
.build();
const collection = new YamlApplicationStub()
.withOs(osText);
const parserMock = mockEnumParser(expectedName, osText, expectedOs);
const env = getProcessEnvironmentStub();
const info = new ProjectInformationStub();
// act
const actual = parseCategoryCollection(collection, env, parserMock);
const actual = parseCategoryCollection(collection, info, parserMock);
// assert
expect(actual.os).to.equal(expectedOs);
});
});
});
});
class YamlApplicationBuilder {
private os = 'windows';
private actions: readonly YamlCategory[] = [ getTestCategory() ];
private scripting: YamlScriptingDefinition = getTestDefinition();
public withActions(actions: readonly YamlCategory[]): YamlApplicationBuilder {
this.actions = actions;
return this;
}
public withOs(os: string): YamlApplicationBuilder {
this.os = os;
return this;
}
public withScripting(scripting: YamlScriptingDefinition): YamlApplicationBuilder {
this.scripting = scripting;
return this;
}
public build(): YamlApplication {
return { os: this.os, scripting: this.scripting, actions: this.actions };
}
}
function getTestDefinition(): YamlScriptingDefinition {
return {
fileExtension: '.bat',
language: ScriptingLanguage[ScriptingLanguage.batchfile],
startCode: 'start',
endCode: 'end',
};
}
function getTestCategory(scriptPrefix = 'testScript'): YamlCategory {
return {
category: 'category name',
children: [
getTestScript(`${scriptPrefix}-standard`, RecommendationLevel.Standard),
getTestScript(`${scriptPrefix}-strict`, RecommendationLevel.Strict),
],
};
}
function getTestScript(scriptName: string, level: RecommendationLevel = RecommendationLevel.Standard): YamlScript {
return {
name: scriptName,
code: 'script code',
revertCode: 'revert code',
recommend: RecommendationLevel[level].toLowerCase(),
call: undefined,
};
}
function getProcessEnvironmentStub(): NodeJS.ProcessEnv {
return {
VUE_APP_VERSION: 'stub-version',
VUE_APP_NAME: 'stub-name',
VUE_APP_REPOSITORY_URL: 'stub-repository-url',
VUE_APP_HOMEPAGE_URL: 'stub-homepage-url',
};
}

View File

@@ -1,6 +1,7 @@
import 'mocha';
import { expect } from 'chai';
import { parseProjectInformation } from '@/application/Parser/ProjectInformationParser';
import { getProcessEnvironmentStub } from '../../stubs/ProcessEnvironmentStub';
describe('ProjectInformationParser', () => {
describe('parseProjectInformation', () => {
@@ -46,13 +47,3 @@ describe('ProjectInformationParser', () => {
});
});
});
function getProcessEnvironmentStub(): NodeJS.ProcessEnv {
return {
VUE_APP_VERSION: 'stub-version',
VUE_APP_NAME: 'stub-name',
VUE_APP_REPOSITORY_URL: 'stub-repository-url',
VUE_APP_HOMEPAGE_URL: 'stub-homepage-url',
};
}

View File

@@ -0,0 +1,118 @@
import 'mocha';
import { expect } from 'chai';
import { Application } from '@/domain/Application';
import { CategoryCollectionStub } from '../stubs/CategoryCollectionStub';
import { ProjectInformationStub } from '../stubs/ProjectInformationStub';
import { OperatingSystem } from '@/domain/OperatingSystem';
describe('Application', () => {
describe('getCollection', () => {
it('returns undefined if not found', () => {
// arrange
const expected = undefined;
const info = new ProjectInformationStub();
const collections = [ new CategoryCollectionStub().withOs(OperatingSystem.Windows) ];
// act
const sut = new Application(info, collections);
const actual = sut.getCollection(OperatingSystem.Android);
// assert
expect(actual).to.equals(expected);
});
it('returns expected when multiple collections exist', () => {
// arrange
const os = OperatingSystem.Windows;
const expected = new CategoryCollectionStub().withOs(os);
const info = new ProjectInformationStub();
const collections = [ expected, new CategoryCollectionStub().withOs(OperatingSystem.Android) ];
// act
const sut = new Application(info, collections);
const actual = sut.getCollection(os);
// assert
expect(actual).to.equals(expected);
});
});
describe('ctor', () => {
describe('info', () => {
it('throws if undefined', () => {
// arrange
const expectedError = 'undefined project information';
const info = undefined;
const collections = [new CategoryCollectionStub()];
// act
const act = () => new Application(info, collections);
// assert
expect(act).to.throw(expectedError);
});
it('sets as expected', () => {
// arrange
const expected = new ProjectInformationStub();
const collections = [new CategoryCollectionStub()];
// act
const sut = new Application(expected, collections);
// assert
expect(sut.info).to.equal(expected);
});
});
describe('collections', () => {
describe('throws on invalid value', () => {
// arrange
const testCases = [
{
name: 'undefined',
expectedError: 'undefined collections',
value: undefined,
},
{
name: 'empty',
expectedError: 'no collection in the list',
value: [],
},
{
name: 'undefined value in list',
expectedError: 'undefined collection in the list',
value: [ new CategoryCollectionStub(), undefined ],
},
{
name: 'two collections with same OS',
expectedError: 'multiple collections with same os: windows',
value: [
new CategoryCollectionStub().withOs(OperatingSystem.Windows),
new CategoryCollectionStub().withOs(OperatingSystem.Windows),
new CategoryCollectionStub().withOs(OperatingSystem.BlackBerry),
],
},
];
for (const testCase of testCases) {
const info = new ProjectInformationStub();
const collections = testCase.value;
// act
const act = () => new Application(info, collections);
// assert
expect(act).to.throw(testCase.expectedError);
}
});
it('sets as expected', () => {
// arrange
const info = new ProjectInformationStub();
const expected = [new CategoryCollectionStub()];
// act
const sut = new Application(info, expected);
// assert
expect(sut.collections).to.equal(expected);
});
});
});
describe('getSupportedOsList', () => {
it('returns expected', () => {
// arrange
const expected = [ OperatingSystem.Windows, OperatingSystem.macOS ];
const info = new ProjectInformationStub();
const collections = expected.map((os) => new CategoryCollectionStub().withOs(os));
// act
const sut = new Application(info, collections);
const actual = sut.getSupportedOsList();
// assert
expect(actual).to.deep.equal(expected);
});
});
});

View File

@@ -1,16 +1,15 @@
import { ScriptStub } from '../stubs/ScriptStub';
import { CategoryStub } from '../stubs/CategoryStub';
import 'mocha';
import { expect } from 'chai';
import { ProjectInformation } from '@/domain/ProjectInformation';
import { IProjectInformation } from '@/domain/IProjectInformation';
import { ICategory } from '@/domain/ICategory';
import { OperatingSystem } from '@/domain/OperatingSystem';
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
import { RecommendationLevel } from '@/domain/RecommendationLevel';
import { getEnumValues } from '@/application/Common/Enum';
import { CategoryCollection } from '../../../src/domain/CategoryCollection';
import { CategoryCollection } from '@/domain/CategoryCollection';
import { ScriptStub } from '../stubs/ScriptStub';
import { CategoryStub } from '../stubs/CategoryStub';
describe('CategoryCollection', () => {
describe('getScriptsByLevel', () => {
@@ -177,31 +176,6 @@ describe('CategoryCollection', () => {
expect(sut.totalCategories).to.equal(expected);
});
});
describe('information', () => {
it('sets information as expected', () => {
// arrange
const expected = new ProjectInformation(
'expected-name', 'expected-repo', '0.31.0', 'expected-homepage');
// act
const sut = new CategoryCollectionBuilder()
.withInfo(expected)
.construct();
// assert
expect(sut.info).to.deep.equal(expected);
});
it('cannot construct without information', () => {
// arrange
const information = undefined;
// act
function construct() {
return new CategoryCollectionBuilder()
.withInfo(information)
.construct();
}
// assert
expect(construct).to.throw('undefined info');
});
});
describe('os', () => {
it('sets os as expected', () => {
// arrange
@@ -281,7 +255,6 @@ function getValidScriptingDefinition(): IScriptingDefinition {
class CategoryCollectionBuilder {
private os = OperatingSystem.Windows;
private info = new ProjectInformation('name', 'repo', '0.1.0', 'homepage');
private actions: readonly ICategory[] = [
new CategoryStub(1).withScripts(
new ScriptStub('S1').withLevel(RecommendationLevel.Standard),
@@ -292,10 +265,6 @@ class CategoryCollectionBuilder {
this.os = os;
return this;
}
public withInfo(info: IProjectInformation): CategoryCollectionBuilder {
this.info = info;
return this;
}
public withActions(actions: readonly ICategory[]): CategoryCollectionBuilder {
this.actions = actions;
return this;
@@ -305,6 +274,6 @@ class CategoryCollectionBuilder {
return this;
}
public construct(): CategoryCollection {
return new CategoryCollection(this.os, this.info, this.actions, this.script);
return new CategoryCollection(this.os, this.actions, this.script);
}
}

View File

@@ -1,7 +1,7 @@
import 'mocha';
import { expect } from 'chai';
import { ScriptCode } from '@/domain/ScriptCode';
import { IScriptCode } from '../../../src/domain/IScriptCode';
import { IScriptCode } from '@/domain/IScriptCode';
describe('ScriptCode', () => {
describe('scriptName', () => {

View File

@@ -0,0 +1,28 @@
import { IApplication } from '@/domain/IApplication';
import { ICategoryCollection } from '@/domain/ICategoryCollection';
import { OperatingSystem } from '@/domain/OperatingSystem';
import { IProjectInformation } from '@/domain/IProjectInformation';
import { ProjectInformationStub } from './ProjectInformationStub';
export class ApplicationStub implements IApplication {
public info: IProjectInformation = new ProjectInformationStub();
public collections: ICategoryCollection[] = [ ];
public getCollection(operatingSystem: OperatingSystem): ICategoryCollection {
return this.collections.find((collection) => collection.os === operatingSystem);
}
public getSupportedOsList(): OperatingSystem[] {
return this.collections.map((collection) => collection.os);
}
public withCollection(collection: ICategoryCollection): ApplicationStub {
this.collections.push(collection);
return this;
}
public withProjectInformation(info: IProjectInformation): ApplicationStub {
this.info = info;
return this;
}
public withCollections(...collections: readonly ICategoryCollection[]): ApplicationStub {
this.collections.push(...collections);
return this;
}
}

View File

@@ -1,5 +1,4 @@
import { ScriptingDefinitionStub } from './ScriptingDefinitionStub';
import { ProjectInformation } from '@/domain/ProjectInformation';
import { OperatingSystem } from '@/domain/OperatingSystem';
import { ScriptStub } from './ScriptStub';
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
@@ -13,7 +12,6 @@ export class CategoryCollectionStub implements ICategoryCollection {
public initialScript: IScript = new ScriptStub('55');
public totalScripts = 0;
public totalCategories = 0;
public readonly info = new ProjectInformation('StubApplication', '0.1.0', 'https://github.com/undergroundwires/privacy.sexy', 'https://privacy.sexy');
public readonly actions = new Array<ICategory>();
public withAction(category: ICategory): CategoryCollectionStub {

View File

@@ -0,0 +1,11 @@
import { IEnvironment } from '@/application/Environment/IEnvironment';
import { OperatingSystem } from '@/domain/OperatingSystem';
export class EnvironmentStub implements IEnvironment {
public isDesktop = true;
public os = OperatingSystem.Windows;
public withOs(os: OperatingSystem): EnvironmentStub {
this.os = os;
return this;
}
}

View File

@@ -0,0 +1,8 @@
export function getProcessEnvironmentStub(): NodeJS.ProcessEnv {
return {
VUE_APP_VERSION: 'stub-version',
VUE_APP_NAME: 'stub-name',
VUE_APP_REPOSITORY_URL: 'stub-repository-url',
VUE_APP_HOMEPAGE_URL: 'stub-homepage-url',
};
}

View File

@@ -2,13 +2,13 @@ import { IProjectInformation } from '@/domain/IProjectInformation';
import { OperatingSystem } from '@/domain/OperatingSystem';
export class ProjectInformationStub implements IProjectInformation {
public name: string;
public version: string;
public repositoryUrl: string;
public homepage: string;
public feedbackUrl: string;
public releaseUrl: string;
public repositoryWebUrl: string;
public name = 'name';
public version = 'version';
public repositoryUrl = 'repositoryUrl';
public homepage = 'homepage';
public feedbackUrl = 'feedbackUrl';
public releaseUrl = 'releaseUrl';
public repositoryWebUrl = 'repositoryWebUrl';
public withName(name: string): ProjectInformationStub {
this.name = name;
return this;

View File

@@ -0,0 +1,53 @@
import { RecommendationLevel } from '@/domain/RecommendationLevel';
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
import { YamlCategory, YamlScript, YamlApplication, YamlScriptingDefinition } from 'js-yaml-loader!@/application/application.yaml';
export class YamlApplicationStub implements YamlApplication {
public os = 'windows';
public actions: readonly YamlCategory[] = [ getCategoryStub() ];
public scripting: YamlScriptingDefinition = getTestDefinitionStub();
public withActions(actions: readonly YamlCategory[]): YamlApplicationStub {
this.actions = actions;
return this;
}
public withOs(os: string): YamlApplicationStub {
this.os = os;
return this;
}
public withScripting(scripting: YamlScriptingDefinition): YamlApplicationStub {
this.scripting = scripting;
return this;
}
}
export function getCategoryStub(scriptPrefix = 'testScript'): YamlCategory {
return {
category: 'category name',
children: [
getScriptStub(`${scriptPrefix}-standard`, RecommendationLevel.Standard),
getScriptStub(`${scriptPrefix}-strict`, RecommendationLevel.Strict),
],
};
}
function getTestDefinitionStub(): YamlScriptingDefinition {
return {
fileExtension: '.bat',
language: ScriptingLanguage[ScriptingLanguage.batchfile],
startCode: 'start',
endCode: 'end',
};
}
function getScriptStub(scriptName: string, level: RecommendationLevel = RecommendationLevel.Standard): YamlScript {
return {
name: scriptName,
code: 'script code',
revertCode: 'revert code',
recommend: RecommendationLevel[level].toLowerCase(),
call: undefined,
};
}