rename Application to CategoryCollection #40

This commit is contained in:
undergroundwires
2021-01-02 03:13:01 +01:00
parent 7cc161c828
commit 6fe858d86a
42 changed files with 350 additions and 311 deletions

View File

@@ -31,7 +31,7 @@
- **Stateless**, extends `Vue` - **Stateless**, extends `Vue`
- **Stateful**, extends [`StatefulVue`](./src/presentation/StatefulVue.ts) - **Stateful**, extends [`StatefulVue`](./src/presentation/StatefulVue.ts)
- The source of truth for the state lies in application layer (`./src/application/`) and must be updated from the views if they're mutating the state - The source of truth for the state lies in application layer (`./src/application/`) and must be updated from the views if they're mutating the state
- They mutate or/and react to changes in [application state](src/application/Context/State/ApplicationState.ts). - They mutate or/and react to changes in [application state](src/application/Context/State/CategoryCollectionState.ts).
- You can react by getting the state and listening to it and update the view accordingly in [`mounted()`](https://vuejs.org/v2/api/#mounted) method. - You can react by getting the state and listening to it and update the view accordingly in [`mounted()`](https://vuejs.org/v2/api/#mounted) method.
## License ## License

View File

@@ -66,7 +66,7 @@
- Event driven as in components simply listens to events from the state and act accordingly. - Event driven as in components simply listens to events from the state and act accordingly.
- **Application Layer** - **Application Layer**
- Keeps the application state - Keeps the application state
- The [state](src/application/Context/State/ApplicationState.ts) is a mutable singleton & event producer. - The [state](src/application/Context/State/CategoryCollectionState.ts) is a mutable singleton & event producer.
- The application is defined & controlled in a [single YAML file](src/application/application.yaml) (see [Data-driven programming](https://en.wikipedia.org/wiki/Data-driven_programming)) - The application is defined & controlled in a [single YAML file](src/application/application.yaml) (see [Data-driven programming](https://en.wikipedia.org/wiki/Data-driven_programming))
![DDD + vue.js](img/architecture/app-ddd.png) ![DDD + vue.js](img/architecture/app-ddd.png)

View File

@@ -1,20 +1,20 @@
import { IApplicationContext } from './IApplicationContext'; import { IApplicationContext } from './IApplicationContext';
import { IApplication } from '@/domain/IApplication'; import { ICategoryCollectionState } from './State/ICategoryCollectionState';
import { IApplicationState } from './State/IApplicationState'; import { CategoryCollectionState } from './State/CategoryCollectionState';
import { ApplicationState } from './State/ApplicationState';
import applicationFile from 'js-yaml-loader!@/application/application.yaml'; import applicationFile from 'js-yaml-loader!@/application/application.yaml';
import { parseApplication } from '../Parser/ApplicationParser'; import { parseCategoryCollection } from '../Parser/CategoryCollectionParser';
import { ICategoryCollection } from '@/domain/ICategoryCollection';
export function createContext(): IApplicationContext { export function createContext(): IApplicationContext {
const application = parseApplication(applicationFile); const application = parseCategoryCollection(applicationFile);
const context = new ApplicationContext(application); const context = new ApplicationContext(application);
return context; return context;
} }
export class ApplicationContext implements IApplicationContext { export class ApplicationContext implements IApplicationContext {
public readonly state: IApplicationState; public readonly state: ICategoryCollectionState;
public constructor(public readonly app: IApplication) { public constructor(public readonly collection: ICategoryCollection) {
this.state = new ApplicationState(app); this.state = new CategoryCollectionState(collection);
} }
} }

View File

@@ -1,9 +1,9 @@
import { ApplicationContext } from './ApplicationContext'; import { ApplicationContext } from './ApplicationContext';
import { IApplicationContext } from '@/application/Context/IApplicationContext'; import { IApplicationContext } from '@/application/Context/IApplicationContext';
import applicationFile from 'js-yaml-loader!@/application/application.yaml'; import applicationFile from 'js-yaml-loader!@/application/application.yaml';
import { parseApplication } from '@/application/Parser/ApplicationParser'; import { parseCategoryCollection } from '@/application/Parser/CategoryCollectionParser';
export function buildContext(): IApplicationContext { export function buildContext(): IApplicationContext {
const application = parseApplication(applicationFile); const application = parseCategoryCollection(applicationFile);
return new ApplicationContext(application); return new ApplicationContext(application);
} }

View File

@@ -1,7 +1,7 @@
import { IApplication } from '@/domain/IApplication'; import { ICategoryCollectionState } from './State/ICategoryCollectionState';
import { IApplicationState } from './State/IApplicationState'; import { ICategoryCollection } from '@/domain/ICategoryCollection';
export interface IApplicationContext { export interface IApplicationContext {
readonly app: IApplication; readonly collection: ICategoryCollection;
readonly state: IApplicationState; readonly state: ICategoryCollectionState;
} }

View File

@@ -3,18 +3,18 @@ import { IUserFilter } from './Filter/IUserFilter';
import { ApplicationCode } from './Code/ApplicationCode'; import { ApplicationCode } from './Code/ApplicationCode';
import { UserSelection } from './Selection/UserSelection'; import { UserSelection } from './Selection/UserSelection';
import { IUserSelection } from './Selection/IUserSelection'; import { IUserSelection } from './Selection/IUserSelection';
import { IApplicationState } from './IApplicationState'; import { ICategoryCollectionState } from './ICategoryCollectionState';
import { IApplication } from '@/domain/IApplication';
import { IApplicationCode } from './Code/IApplicationCode'; import { IApplicationCode } from './Code/IApplicationCode';
import { ICategoryCollection } from '../../../domain/ICategoryCollection';
export class ApplicationState implements IApplicationState { export class CategoryCollectionState implements ICategoryCollectionState {
public readonly code: IApplicationCode; public readonly code: IApplicationCode;
public readonly selection: IUserSelection; public readonly selection: IUserSelection;
public readonly filter: IUserFilter; public readonly filter: IUserFilter;
public constructor(readonly app: IApplication) { public constructor(readonly collection: ICategoryCollection) {
this.selection = new UserSelection(app, []); this.selection = new UserSelection(collection, []);
this.code = new ApplicationCode(this.selection, app.scripting); this.code = new ApplicationCode(this.selection, collection.scripting);
this.filter = new UserFilter(app); this.filter = new UserFilter(collection);
} }
} }

View File

@@ -1,16 +1,16 @@
import { IScript } from '@/domain/IScript'; import { IScript } from '@/domain/IScript';
import { FilterResult } from './FilterResult'; import { FilterResult } from './FilterResult';
import { IFilterResult } from './IFilterResult'; import { IFilterResult } from './IFilterResult';
import { IApplication } from '@/domain/IApplication';
import { IUserFilter } from './IUserFilter'; import { IUserFilter } from './IUserFilter';
import { Signal } from '@/infrastructure/Events/Signal'; import { Signal } from '@/infrastructure/Events/Signal';
import { ICategoryCollection } from '@/domain/ICategoryCollection';
export class UserFilter implements IUserFilter { export class UserFilter implements IUserFilter {
public readonly filtered = new Signal<IFilterResult>(); public readonly filtered = new Signal<IFilterResult>();
public readonly filterRemoved = new Signal<void>(); public readonly filterRemoved = new Signal<void>();
public currentFilter: IFilterResult | undefined; public currentFilter: IFilterResult | undefined;
constructor(private application: IApplication) { constructor(private collection: ICategoryCollection) {
} }
@@ -19,9 +19,9 @@ export class UserFilter implements IUserFilter {
throw new Error('Filter must be defined and not empty. Use removeFilter() to remove the filter'); throw new Error('Filter must be defined and not empty. Use removeFilter() to remove the filter');
} }
const filterLowercase = filter.toLocaleLowerCase(); const filterLowercase = filter.toLocaleLowerCase();
const filteredScripts = this.application.getAllScripts().filter( const filteredScripts = this.collection.getAllScripts().filter(
(script) => isScriptAMatch(script, filterLowercase)); (script) => isScriptAMatch(script, filterLowercase));
const filteredCategories = this.application.getAllCategories().filter( const filteredCategories = this.collection.getAllCategories().filter(
(category) => category.name.toLowerCase().includes(filterLowercase)); (category) => category.name.toLowerCase().includes(filterLowercase));
const matches = new FilterResult( const matches = new FilterResult(
filteredScripts, filteredScripts,

View File

@@ -3,7 +3,7 @@ import { IUserSelection } from './Selection/IUserSelection';
import { IApplicationCode } from './Code/IApplicationCode'; import { IApplicationCode } from './Code/IApplicationCode';
export { IUserSelection, IApplicationCode, IUserFilter }; export { IUserSelection, IApplicationCode, IUserFilter };
export interface IApplicationState { export interface ICategoryCollectionState {
readonly code: IApplicationCode; readonly code: IApplicationCode;
readonly filter: IUserFilter; readonly filter: IUserFilter;
readonly selection: IUserSelection; readonly selection: IUserSelection;

View File

@@ -1,17 +1,18 @@
import { SelectedScript } from './SelectedScript'; import { SelectedScript } from './SelectedScript';
import { IApplication, ICategory } from '@/domain/IApplication';
import { IUserSelection } from './IUserSelection'; import { IUserSelection } from './IUserSelection';
import { InMemoryRepository } from '@/infrastructure/Repository/InMemoryRepository'; import { InMemoryRepository } from '@/infrastructure/Repository/InMemoryRepository';
import { IScript } from '@/domain/IScript'; import { IScript } from '@/domain/IScript';
import { Signal } from '@/infrastructure/Events/Signal'; import { Signal } from '@/infrastructure/Events/Signal';
import { IRepository } from '@/infrastructure/Repository/IRepository'; import { IRepository } from '@/infrastructure/Repository/IRepository';
import { ICategory } from '@/domain/ICategory';
import { ICategoryCollection } from '@/domain/ICategoryCollection';
export class UserSelection implements IUserSelection { export class UserSelection implements IUserSelection {
public readonly changed = new Signal<ReadonlyArray<SelectedScript>>(); public readonly changed = new Signal<ReadonlyArray<SelectedScript>>();
private readonly scripts: IRepository<string, SelectedScript>; private readonly scripts: IRepository<string, SelectedScript>;
constructor( constructor(
private readonly app: IApplication, private readonly collection: ICategoryCollection,
selectedScripts: ReadonlyArray<SelectedScript>) { selectedScripts: ReadonlyArray<SelectedScript>) {
this.scripts = new InMemoryRepository<string, SelectedScript>(); this.scripts = new InMemoryRepository<string, SelectedScript>();
if (selectedScripts && selectedScripts.length > 0) { if (selectedScripts && selectedScripts.length > 0) {
@@ -40,7 +41,7 @@ export class UserSelection implements IUserSelection {
} }
public removeAllInCategory(categoryId: number): void { public removeAllInCategory(categoryId: number): void {
const category = this.app.findCategory(categoryId); const category = this.collection.findCategory(categoryId);
const scriptsToRemove = category.getAllScriptsRecursively() const scriptsToRemove = category.getAllScriptsRecursively()
.filter((script) => this.scripts.exists(script.id)); .filter((script) => this.scripts.exists(script.id));
if (!scriptsToRemove.length) { if (!scriptsToRemove.length) {
@@ -53,7 +54,7 @@ export class UserSelection implements IUserSelection {
} }
public addOrUpdateAllInCategory(categoryId: number, revert: boolean = false): void { public addOrUpdateAllInCategory(categoryId: number, revert: boolean = false): void {
const category = this.app.findCategory(categoryId); const category = this.collection.findCategory(categoryId);
const scriptsToAddOrUpdate = category.getAllScriptsRecursively() const scriptsToAddOrUpdate = category.getAllScriptsRecursively()
.filter((script) => .filter((script) =>
!this.scripts.exists(script.id) !this.scripts.exists(script.id)
@@ -70,7 +71,7 @@ export class UserSelection implements IUserSelection {
} }
public addSelectedScript(scriptId: string, revert: boolean): void { public addSelectedScript(scriptId: string, revert: boolean): void {
const script = this.app.findScript(scriptId); const script = this.collection.findScript(scriptId);
if (!script) { if (!script) {
throw new Error(`Cannot add (id: ${scriptId}) as it is unknown`); throw new Error(`Cannot add (id: ${scriptId}) as it is unknown`);
} }
@@ -80,7 +81,7 @@ export class UserSelection implements IUserSelection {
} }
public addOrUpdateSelectedScript(scriptId: string, revert: boolean): void { public addOrUpdateSelectedScript(scriptId: string, revert: boolean): void {
const script = this.app.findScript(scriptId); const script = this.collection.findScript(scriptId);
const selectedScript = new SelectedScript(script, revert); const selectedScript = new SelectedScript(script, revert);
this.scripts.addOrUpdateItem(selectedScript); this.scripts.addOrUpdateItem(selectedScript);
this.changed.notify(this.scripts.getItems()); this.changed.notify(this.scripts.getItems());
@@ -105,7 +106,7 @@ export class UserSelection implements IUserSelection {
} }
public selectAll(): void { public selectAll(): void {
for (const script of this.app.getAllScripts()) { for (const script of this.collection.getAllScripts()) {
if (!this.scripts.exists(script.id)) { if (!this.scripts.exists(script.id)) {
const selection = new SelectedScript(script, false); const selection = new SelectedScript(script, false);
this.scripts.addItem(selection); this.scripts.addItem(selection);

View File

@@ -1,6 +1,4 @@
import { Category } from '@/domain/Category'; import { Category } from '@/domain/Category';
import { Application } from '@/domain/Application';
import { IApplication } from '@/domain/IApplication';
import { YamlApplication } from 'js-yaml-loader!@/application.yaml'; import { YamlApplication } from 'js-yaml-loader!@/application.yaml';
import { parseCategory } from './CategoryParser'; import { parseCategory } from './CategoryParser';
import { parseProjectInformation } from './ProjectInformationParser'; import { parseProjectInformation } from './ProjectInformationParser';
@@ -8,11 +6,13 @@ import { ScriptCompiler } from './Compiler/ScriptCompiler';
import { OperatingSystem } from '@/domain/OperatingSystem'; import { OperatingSystem } from '@/domain/OperatingSystem';
import { parseScriptingDefinition } from './ScriptingDefinitionParser'; import { parseScriptingDefinition } from './ScriptingDefinitionParser';
import { createEnumParser } from '../Common/Enum'; import { createEnumParser } from '../Common/Enum';
import { ICategoryCollection } from '@/domain/ICategoryCollection';
import { CategoryCollection } from '@/domain/CategoryCollection';
export function parseApplication( export function parseCategoryCollection(
content: YamlApplication, content: YamlApplication,
env: NodeJS.ProcessEnv = process.env, env: NodeJS.ProcessEnv = process.env,
osParser = createEnumParser(OperatingSystem)): IApplication { osParser = createEnumParser(OperatingSystem)): ICategoryCollection {
validate(content); validate(content);
const compiler = new ScriptCompiler(content.functions); const compiler = new ScriptCompiler(content.functions);
const categories = new Array<Category>(); const categories = new Array<Category>();
@@ -23,19 +23,19 @@ export function parseApplication(
const os = osParser.parseEnum(content.os, 'os'); const os = osParser.parseEnum(content.os, 'os');
const info = parseProjectInformation(env); const info = parseProjectInformation(env);
const scripting = parseScriptingDefinition(content.scripting, info); const scripting = parseScriptingDefinition(content.scripting, info);
const app = new Application( const collection = new CategoryCollection(
os, os,
info, info,
categories, categories,
scripting); scripting);
return app; return collection;
} }
function validate(content: YamlApplication): void { function validate(content: YamlApplication): void {
if (!content) { if (!content) {
throw new Error('application is null or undefined'); throw new Error('content is null or undefined');
} }
if (!content.actions || content.actions.length <= 0) { if (!content.actions || content.actions.length <= 0) {
throw new Error('application does not define any action'); throw new Error('content does not define any action');
} }
} }

View File

@@ -2,17 +2,17 @@ import { getEnumNames, getEnumValues } from '@/application/Common/Enum';
import { IEntity } from '../infrastructure/Entity/IEntity'; import { IEntity } from '../infrastructure/Entity/IEntity';
import { ICategory } from './ICategory'; import { ICategory } from './ICategory';
import { IScript } from './IScript'; import { IScript } from './IScript';
import { IApplication } from './IApplication';
import { IProjectInformation } from './IProjectInformation'; import { IProjectInformation } from './IProjectInformation';
import { RecommendationLevel } from './RecommendationLevel'; import { RecommendationLevel } from './RecommendationLevel';
import { OperatingSystem } from './OperatingSystem'; import { OperatingSystem } from './OperatingSystem';
import { IScriptingDefinition } from './IScriptingDefinition'; import { IScriptingDefinition } from './IScriptingDefinition';
import { ICategoryCollection } from './ICategoryCollection';
export class Application implements IApplication { export class CategoryCollection implements ICategoryCollection {
public get totalScripts(): number { return this.queryable.allScripts.length; } public get totalScripts(): number { return this.queryable.allScripts.length; }
public get totalCategories(): number { return this.queryable.allCategories.length; } public get totalCategories(): number { return this.queryable.allCategories.length; }
private readonly queryable: IQueryableApplication; private readonly queryable: IQueryableCollection;
constructor( constructor(
public readonly os: OperatingSystem, public readonly os: OperatingSystem,
@@ -89,26 +89,26 @@ function ensureNoDuplicates<TKey>(entities: ReadonlyArray<IEntity<TKey>>) {
} }
} }
interface IQueryableApplication { interface IQueryableCollection {
allCategories: ICategory[]; allCategories: ICategory[];
allScripts: IScript[]; allScripts: IScript[];
scriptsByLevel: Map<RecommendationLevel, readonly IScript[]>; scriptsByLevel: Map<RecommendationLevel, readonly IScript[]>;
} }
function ensureValid(application: IQueryableApplication) { function ensureValid(application: IQueryableCollection) {
ensureValidCategories(application.allCategories); ensureValidCategories(application.allCategories);
ensureValidScripts(application.allScripts); ensureValidScripts(application.allScripts);
} }
function ensureValidCategories(allCategories: readonly ICategory[]) { function ensureValidCategories(allCategories: readonly ICategory[]) {
if (!allCategories || allCategories.length === 0) { if (!allCategories || allCategories.length === 0) {
throw new Error('Application must consist of at least one category'); throw new Error('must consist of at least one category');
} }
} }
function ensureValidScripts(allScripts: readonly IScript[]) { function ensureValidScripts(allScripts: readonly IScript[]) {
if (!allScripts || allScripts.length === 0) { if (!allScripts || allScripts.length === 0) {
throw new Error('Application must consist of at least one script'); throw new Error('must consist of at least one script');
} }
for (const level of getEnumValues(RecommendationLevel)) { for (const level of getEnumValues(RecommendationLevel)) {
if (allScripts.every((script) => script.level !== level)) { if (allScripts.every((script) => script.level !== level)) {
@@ -130,7 +130,7 @@ function flattenApplication(categories: ReadonlyArray<ICategory>): [ICategory[],
function flattenCategories( function flattenCategories(
categories: ReadonlyArray<ICategory>, categories: ReadonlyArray<ICategory>,
allCategories: ICategory[], allCategories: ICategory[],
allScripts: IScript[]): IQueryableApplication { allScripts: IScript[]): IQueryableCollection {
if (!categories || categories.length === 0) { if (!categories || categories.length === 0) {
return; return;
} }
@@ -153,7 +153,7 @@ function flattenScripts(
} }
function makeQueryable( function makeQueryable(
actions: ReadonlyArray<ICategory>): IQueryableApplication { actions: ReadonlyArray<ICategory>): IQueryableCollection {
const flattened = flattenApplication(actions); const flattened = flattenApplication(actions);
return { return {
allCategories: flattened[0], allCategories: flattened[0],

View File

@@ -1,14 +1,13 @@
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
import { OperatingSystem } from '@/domain/OperatingSystem';
import { RecommendationLevel } from '@/domain/RecommendationLevel';
import { IScript } from '@/domain/IScript'; import { IScript } from '@/domain/IScript';
import { ICategory } from '@/domain/ICategory'; import { ICategory } from '@/domain/ICategory';
import { IProjectInformation } from './IProjectInformation'; import { IProjectInformation } from '@/domain/IProjectInformation';
import { RecommendationLevel } from './RecommendationLevel';
import { OperatingSystem } from './OperatingSystem';
import { IScriptingDefinition } from './IScriptingDefinition';
export interface IApplication { export interface ICategoryCollection {
readonly info: IProjectInformation; readonly info: IProjectInformation;
readonly scripting: IScriptingDefinition; readonly scripting: IScriptingDefinition;
readonly os: OperatingSystem; readonly os: OperatingSystem;
readonly totalScripts: number; readonly totalScripts: number;
readonly totalCategories: number; readonly totalCategories: number;
@@ -20,6 +19,3 @@ export interface IApplication {
getAllScripts(): ReadonlyArray<IScript>; getAllScripts(): ReadonlyArray<IScript>;
getAllCategories(): ReadonlyArray<ICategory>; getAllCategories(): ReadonlyArray<ICategory>;
} }
export { IScript } from '@/domain/IScript';
export { ICategory } from '@/domain/ICategory';

View File

@@ -33,7 +33,7 @@ export default class CardList extends StatefulVue {
public async mounted() { public async mounted() {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContextAsync();
this.setCategories(context.app.actions); this.setCategories(context.collection.actions);
this.onOutsideOfActiveCardClicked((element) => { this.onOutsideOfActiveCardClicked((element) => {
if (hasDirective(element)) { if (hasDirective(element)) {
return; return;

View File

@@ -79,7 +79,7 @@ export default class CardListItem extends StatefulVue {
@Watch('categoryId') @Watch('categoryId')
public async updateStateAsync(value: |number) { public async updateStateAsync(value: |number) {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContextAsync();
const category = !value ? undefined : context.app.findCategory(this.categoryId); const category = !value ? undefined : context.collection.findCategory(this.categoryId);
this.cardTitle = category ? category.name : undefined; this.cardTitle = category ? category.name : undefined;
const currentSelection = context.state.selection; const currentSelection = context.state.selection;
this.isAnyChildSelected = category ? currentSelection.isAnySelected(category) : false; this.isAnyChildSelected = category ? currentSelection.isAnySelected(category) : false;

View File

@@ -1,18 +1,18 @@
import { IApplication } from './../../../domain/IApplication';
import { ICategory, IScript } from '@/domain/ICategory'; import { ICategory, IScript } from '@/domain/ICategory';
import { INode, NodeType } from './SelectableTree/Node/INode'; import { INode, NodeType } from './SelectableTree/Node/INode';
import { ICategoryCollection } from '@/domain/ICategoryCollection';
export function parseAllCategories(app: IApplication): INode[] | undefined { export function parseAllCategories(collection: ICategoryCollection): INode[] | undefined {
const nodes = new Array<INode>(); const nodes = new Array<INode>();
for (const category of app.actions) { for (const category of collection.actions) {
const children = parseCategoryRecursively(category); const children = parseCategoryRecursively(category);
nodes.push(convertCategoryToNode(category, children)); nodes.push(convertCategoryToNode(category, children));
} }
return nodes; return nodes;
} }
export function parseSingleCategory(categoryId: number, app: IApplication): INode[] | undefined { export function parseSingleCategory(categoryId: number, collection: ICategoryCollection): INode[] | undefined {
const category = app.findCategory(categoryId); const category = collection.findCategory(categoryId);
if (!category) { if (!category) {
throw new Error(`Category with id ${categoryId} does not exist`); throw new Error(`Category with id ${categoryId} does not exist`);
} }

View File

@@ -19,7 +19,7 @@
import { StatefulVue } from '@/presentation/StatefulVue'; import { StatefulVue } from '@/presentation/StatefulVue';
import { IScript } from '@/domain/IScript'; import { IScript } from '@/domain/IScript';
import { ICategory } from '@/domain/ICategory'; import { ICategory } from '@/domain/ICategory';
import { IApplicationState } from '@/application/Context/State/IApplicationState'; import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
import { IFilterResult } from '@/application/Context/State/Filter/IFilterResult'; import { IFilterResult } from '@/application/Context/State/Filter/IFilterResult';
import { parseAllCategories, parseSingleCategory, getScriptNodeId, getCategoryNodeId, getCategoryId, getScriptId } from './ScriptNodeParser'; import { parseAllCategories, parseSingleCategory, getScriptNodeId, getCategoryNodeId, getCategoryId, getScriptId } from './ScriptNodeParser';
import SelectableTree from './SelectableTree/SelectableTree.vue'; import SelectableTree from './SelectableTree/SelectableTree.vue';
@@ -65,9 +65,9 @@
public async initializeNodesAsync(categoryId?: number) { public async initializeNodesAsync(categoryId?: number) {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContextAsync();
if (categoryId) { if (categoryId) {
this.nodes = parseSingleCategory(categoryId, context.app); this.nodes = parseSingleCategory(categoryId, context.collection);
} else { } else {
this.nodes = parseAllCategories(context.app); this.nodes = parseAllCategories(context.collection);
} }
this.selectedNodeIds = context.state.selection.selectedScripts this.selectedNodeIds = context.state.selection.selectedScripts
.map((selected) => getScriptNodeId(selected.script)); .map((selected) => getScriptNodeId(selected.script));
@@ -80,13 +80,13 @@
(category: ICategory) => node.id === getCategoryNodeId(category)); (category: ICategory) => node.id === getCategoryNodeId(category));
} }
private beginReactingToStateChanges(state: IApplicationState) { private beginReactingToStateChanges(state: ICategoryCollectionState) {
state.selection.changed.on(this.handleSelectionChanged); state.selection.changed.on(this.handleSelectionChanged);
state.filter.filterRemoved.on(this.handleFilterRemoved); state.filter.filterRemoved.on(this.handleFilterRemoved);
state.filter.filtered.on(this.handleFiltered); state.filter.filtered.on(this.handleFiltered);
} }
private setInitialState(state: IApplicationState) { private setInitialState(state: ICategoryCollectionState) {
this.initializeNodesAsync(this.categoryId); this.initializeNodesAsync(this.categoryId);
this.initializeFilter(state.filter.currentFilter); this.initializeFilter(state.filter.currentFilter);
} }
@@ -114,7 +114,7 @@
} }
} }
function toggleCategoryNodeSelection(event: INodeSelectedEvent, state: IApplicationState): void { function toggleCategoryNodeSelection(event: INodeSelectedEvent, state: ICategoryCollectionState): void {
const categoryId = getCategoryId(event.node.id); const categoryId = getCategoryId(event.node.id);
if (event.isSelected) { if (event.isSelected) {
state.selection.addOrUpdateAllInCategory(categoryId, false); state.selection.addOrUpdateAllInCategory(categoryId, false);
@@ -122,7 +122,7 @@
state.selection.removeAllInCategory(categoryId); state.selection.removeAllInCategory(categoryId);
} }
} }
function toggleScriptNodeSelection(event: INodeSelectedEvent, state: IApplicationState): void { function toggleScriptNodeSelection(event: INodeSelectedEvent, state: ICategoryCollectionState): void {
const scriptId = getScriptId(event.node.id); const scriptId = getScriptId(event.node.id);
const actualToggleState = state.selection.isSelected(scriptId); const actualToggleState = state.selection.isSelected(scriptId);
const targetToggleState = event.isSelected; const targetToggleState = event.isSelected;

View File

@@ -37,7 +37,7 @@
@Watch('node') public async onNodeChangedAsync(node: INode) { @Watch('node') public async onNodeChangedAsync(node: INode) {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContextAsync();
this.handler = getReverter(node, context.app); this.handler = getReverter(node, context.collection);
} }
public async onRevertToggledAsync() { public async onRevertToggledAsync() {

View File

@@ -1,16 +1,16 @@
import { IReverter } from './IReverter'; import { IReverter } from './IReverter';
import { getCategoryId } from '../../../ScriptNodeParser'; import { getCategoryId } from '../../../ScriptNodeParser';
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript'; import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
import { IApplication } from '@/domain/IApplication';
import { ScriptReverter } from './ScriptReverter'; import { ScriptReverter } from './ScriptReverter';
import { IUserSelection } from '@/application/Context/State/Selection/IUserSelection'; import { IUserSelection } from '@/application/Context/State/Selection/IUserSelection';
import { ICategoryCollection } from '@/domain/ICategoryCollection';
export class CategoryReverter implements IReverter { export class CategoryReverter implements IReverter {
private readonly categoryId: number; private readonly categoryId: number;
private readonly scriptReverters: ReadonlyArray<ScriptReverter>; private readonly scriptReverters: ReadonlyArray<ScriptReverter>;
constructor(nodeId: string, app: IApplication) { constructor(nodeId: string, collection: ICategoryCollection) {
this.categoryId = getCategoryId(nodeId); this.categoryId = getCategoryId(nodeId);
this.scriptReverters = getAllSubScriptReverters(this.categoryId, app); this.scriptReverters = getAllSubScriptReverters(this.categoryId, collection);
} }
public getState(selectedScripts: ReadonlyArray<SelectedScript>): boolean { public getState(selectedScripts: ReadonlyArray<SelectedScript>): boolean {
return this.scriptReverters.every((script) => script.getState(selectedScripts)); return this.scriptReverters.every((script) => script.getState(selectedScripts));
@@ -20,8 +20,8 @@ export class CategoryReverter implements IReverter {
} }
} }
function getAllSubScriptReverters(categoryId: number, app: IApplication) { function getAllSubScriptReverters(categoryId: number, collection: ICategoryCollection) {
const category = app.findCategory(categoryId); const category = collection.findCategory(categoryId);
if (!category) { if (!category) {
throw new Error(`Category with id "${categoryId}" does not exist`); throw new Error(`Category with id "${categoryId}" does not exist`);
} }

View File

@@ -1,5 +1,5 @@
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript'; import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
import { IUserSelection } from '@/application/Context/State/IApplicationState'; import { IUserSelection } from '@/application/Context/State/ICategoryCollectionState';
export interface IReverter { export interface IReverter {
getState(selectedScripts: ReadonlyArray<SelectedScript>): boolean; getState(selectedScripts: ReadonlyArray<SelectedScript>): boolean;

View File

@@ -1,13 +1,13 @@
import { INode, NodeType } from '../INode'; import { INode, NodeType } from '../INode';
import { IReverter } from './IReverter'; import { IReverter } from './IReverter';
import { ScriptReverter } from './ScriptReverter'; import { ScriptReverter } from './ScriptReverter';
import { IApplication } from '@/domain/IApplication';
import { CategoryReverter } from './CategoryReverter'; import { CategoryReverter } from './CategoryReverter';
import { ICategoryCollection } from '@/domain/ICategoryCollection';
export function getReverter(node: INode, app: IApplication): IReverter { export function getReverter(node: INode, collection: ICategoryCollection): IReverter {
switch (node.type) { switch (node.type) {
case NodeType.Category: case NodeType.Category:
return new CategoryReverter(node.id, app); return new CategoryReverter(node.id, collection);
case NodeType.Script: case NodeType.Script:
return new ScriptReverter(node.id); return new ScriptReverter(node.id);
default: default:

View File

@@ -1,7 +1,7 @@
import { IReverter } from './IReverter'; import { IReverter } from './IReverter';
import { getScriptId } from '../../../ScriptNodeParser'; import { getScriptId } from '../../../ScriptNodeParser';
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript'; import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
import { IUserSelection } from '@/application/Context/State/IApplicationState'; import { IUserSelection } from '@/application/Context/State/ICategoryCollectionState';
export class ScriptReverter implements IReverter { export class ScriptReverter implements IReverter {
private readonly scriptId: string; private readonly scriptId: string;

View File

@@ -105,13 +105,13 @@ const selectors = new Map<SelectionState, ITypeSelector>([
[SelectionState.Standard, { [SelectionState.Standard, {
select: (context) => select: (context) =>
context.state.selection.selectOnly( context.state.selection.selectOnly(
context.app.getScriptsByLevel(RecommendationLevel.Standard)), context.collection.getScriptsByLevel(RecommendationLevel.Standard)),
isSelected: (context) => isSelected: (context) =>
hasAllSelectedLevelOf(RecommendationLevel.Standard, context), hasAllSelectedLevelOf(RecommendationLevel.Standard, context),
}], }],
[SelectionState.Strict, { [SelectionState.Strict, {
select: (context) => select: (context) =>
context.state.selection.selectOnly(context.app.getScriptsByLevel(RecommendationLevel.Strict)), context.state.selection.selectOnly(context.collection.getScriptsByLevel(RecommendationLevel.Strict)),
isSelected: (context) => isSelected: (context) =>
hasAllSelectedLevelOf(RecommendationLevel.Strict, context), hasAllSelectedLevelOf(RecommendationLevel.Strict, context),
}], }],
@@ -119,7 +119,7 @@ const selectors = new Map<SelectionState, ITypeSelector>([
select: (context) => select: (context) =>
context.state.selection.selectAll(), context.state.selection.selectAll(),
isSelected: (context) => isSelected: (context) =>
context.state.selection.totalSelected === context.app.totalScripts, context.state.selection.totalSelected === context.collection.totalScripts,
}], }],
]); ]);
@@ -138,7 +138,7 @@ function getCurrentSelectionState(context: IApplicationContext): SelectionState
} }
function hasAllSelectedLevelOf(level: RecommendationLevel, context: IApplicationContext) { function hasAllSelectedLevelOf(level: RecommendationLevel, context: IApplicationContext) {
const scripts = context.app.getScriptsByLevel(level); const scripts = context.collection.getScriptsByLevel(level);
const selectedScripts = context.state.selection.selectedScripts; const selectedScripts = context.state.selection.selectedScripts;
return areAllSelected(scripts, selectedScripts); return areAllSelected(scripts, selectedScripts);
} }

View File

@@ -74,7 +74,7 @@
public async mounted() { public async mounted() {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContextAsync();
this.repositoryUrl = context.app.info.repositoryWebUrl; this.repositoryUrl = context.collection.info.repositoryWebUrl;
const filter = context.state.filter; const filter = context.state.filter;
filter.filterRemoved.on(() => { filter.filterRemoved.on(() => {
this.isSearching = false; this.isSearching = false;

View File

@@ -40,7 +40,7 @@ export default class TheCodeArea extends StatefulVue {
public async mounted() { public async mounted() {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContextAsync();
this.editor = initializeEditor(this.theme, this.editorId, context.app.scripting.language); this.editor = initializeEditor(this.theme, this.editorId, context.collection.scripting.language);
const appCode = context.state.code; const appCode = context.state.code;
this.editor.setValue(appCode.current || NothingChosenCode, 1); this.editor.setValue(appCode.current || NothingChosenCode, 1);
appCode.changed.on((code) => this.updateCode(code)); appCode.changed.on((code) => this.updateCode(code));

View File

@@ -21,7 +21,7 @@ import { SaveFileDialog, FileType } from '@/infrastructure/SaveFileDialog';
import { Clipboard } from '@/infrastructure/Clipboard'; import { Clipboard } from '@/infrastructure/Clipboard';
import IconButton from './IconButton.vue'; import IconButton from './IconButton.vue';
import { Environment } from '@/application/Environment/Environment'; import { Environment } from '@/application/Environment/Environment';
import { IApplicationCode } from '@/application/Context/State/IApplicationState'; import { IApplicationCode } from '@/application/Context/State/ICategoryCollectionState';
import { ScriptingLanguage } from '@/domain/ScriptingLanguage'; import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
import { IApplicationContext } from '@/application/Context/IApplicationContext'; import { IApplicationContext } from '@/application/Context/IApplicationContext';
@@ -61,9 +61,9 @@ export default class TheCodeButtons extends StatefulVue {
} }
function saveCode(context: IApplicationContext) { function saveCode(context: IApplicationContext) {
const fileName = `privacy-script.${context.app.scripting.fileExtension}`; const fileName = `privacy-script.${context.collection.scripting.fileExtension}`;
const content = context.state.code.current; const content = context.state.code.current;
const type = getType(context.app.scripting.language); const type = getType(context.collection.scripting.language);
SaveFileDialog.saveFile(content, fileName, type); SaveFileDialog.saveFile(content, fileName, type);
} }

View File

@@ -39,7 +39,7 @@ export default class DownloadUrlListItem extends StatefulVue {
private async getDownloadUrlAsync(os: OperatingSystem): Promise<string> { private async getDownloadUrlAsync(os: OperatingSystem): Promise<string> {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContextAsync();
return context.app.info.getDownloadUrl(os); return context.collection.info.getDownloadUrl(os);
} }
} }

View File

@@ -48,8 +48,8 @@ export default class TheFooter extends StatefulVue {
public async mounted() { public async mounted() {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContextAsync();
this.repositoryUrl = context.app.info.repositoryWebUrl; this.repositoryUrl = context.collection.info.repositoryWebUrl;
this.feedbackUrl = context.app.info.feedbackUrl; this.feedbackUrl = context.collection.info.feedbackUrl;
} }
} }
</script> </script>

View File

@@ -74,8 +74,8 @@ export default class TheFooter extends StatefulVue {
} }
public async mounted() { public async mounted() {
const state = await this.getCurrentContextAsync(); const context = await this.getCurrentContextAsync();
const info = state.app.info; const info = context.collection.info;
this.version = info.version; this.version = info.version;
this.homepageUrl = info.homepage; this.homepageUrl = info.homepage;
this.repositoryUrl = info.repositoryWebUrl; this.repositoryUrl = info.repositoryWebUrl;

View File

@@ -16,7 +16,7 @@ export default class TheHeader extends StatefulVue {
public async mounted() { public async mounted() {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContextAsync();
this.title = context.app.info.name; this.title = context.collection.info.name;
} }
} }
</script> </script>

View File

@@ -13,7 +13,7 @@
import { Component, Watch } from 'vue-property-decorator'; import { Component, Watch } from 'vue-property-decorator';
import { StatefulVue } from './StatefulVue'; import { StatefulVue } from './StatefulVue';
import { NonCollapsing } from '@/presentation/Scripts/Cards/NonCollapsingDirective'; import { NonCollapsing } from '@/presentation/Scripts/Cards/NonCollapsingDirective';
import { IUserFilter } from '@/application/Context/State/IApplicationState'; import { IUserFilter } from '@/application/Context/State/ICategoryCollectionState';
@Component( { @Component( {
directives: { NonCollapsing }, directives: { NonCollapsing },
@@ -25,7 +25,7 @@ export default class TheSearchBar extends StatefulVue {
public async mounted() { public async mounted() {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContextAsync();
const totalScripts = context.app.totalScripts; const totalScripts = context.collection.totalScripts;
this.searchPlaceHolder = `Search in ${totalScripts} scripts`; this.searchPlaceHolder = `Search in ${totalScripts} scripts`;
this.beginReacting(context.state.filter); this.beginReacting(context.state.filter);
} }

View File

@@ -1,19 +1,19 @@
import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
import { ApplicationCode } from '@/application/Context/State/Code/ApplicationCode';
import { ScriptStub } from '../../../stubs/ScriptStub';
import { CategoryStub } from '../../../stubs/CategoryStub';
import { ApplicationStub } from '../../../stubs/ApplicationStub';
import 'mocha'; import 'mocha';
import { expect } from 'chai'; import { expect } from 'chai';
import { ApplicationState } from '@/application/Context/State/ApplicationState'; import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
import { ApplicationCode } from '@/application/Context/State/Code/ApplicationCode';
import { CategoryCollectionState } from '@/application/Context/State/CategoryCollectionState';
import { IScript } from '@/domain/IScript'; import { IScript } from '@/domain/IScript';
import { ScriptStub } from '../../../stubs/ScriptStub';
import { CategoryStub } from '../../../stubs/CategoryStub';
import { CategoryCollectionStub } from '../../../stubs/CategoryCollectionStub';
describe('ApplicationState', () => { describe('CategoryCollectionState', () => {
describe('code', () => { describe('code', () => {
it('initialized with empty code', () => { it('initialized with empty code', () => {
// arrange // arrange
const app = new ApplicationStub(); const collection = new CategoryCollectionStub();
const sut = new ApplicationState(app); const sut = new CategoryCollectionState(collection);
// act // act
const code = sut.code.current; const code = sut.code.current;
// assert // assert
@@ -21,13 +21,13 @@ describe('ApplicationState', () => {
}); });
it('reacts to selection changes as expected', () => { it('reacts to selection changes as expected', () => {
// arrange // arrange
const app = new ApplicationStub().withAction(new CategoryStub(0).withScriptIds('scriptId')); const collection = new CategoryCollectionStub().withAction(new CategoryStub(0).withScriptIds('scriptId'));
const selectionStub = new UserSelection(app, []); const selectionStub = new UserSelection(collection, []);
const expectedCodeGenerator = new ApplicationCode(selectionStub, app.scripting); const expectedCodeGenerator = new ApplicationCode(selectionStub, collection.scripting);
selectionStub.selectAll(); selectionStub.selectAll();
const expectedCode = expectedCodeGenerator.current; const expectedCode = expectedCodeGenerator.current;
// act // act
const sut = new ApplicationState(app); const sut = new CategoryCollectionState(collection);
sut.selection.selectAll(); sut.selection.selectAll();
const actualCode = sut.code.current; const actualCode = sut.code.current;
// assert // assert
@@ -37,18 +37,19 @@ describe('ApplicationState', () => {
describe('selection', () => { describe('selection', () => {
it('initialized with no selection', () => { it('initialized with no selection', () => {
// arrange // arrange
const app = new ApplicationStub(); const collection = new CategoryCollectionStub();
const sut = new ApplicationState(app); const sut = new CategoryCollectionState(collection);
// act // act
const actual = sut.selection.totalSelected; const actual = sut.selection.totalSelected;
// assert // assert
expect(actual).to.equal(0); expect(actual).to.equal(0);
}); });
it('can select a script from current application', () => { it('can select a script from current collection', () => {
// arrange // arrange
const expectedScript = new ScriptStub('scriptId'); const expectedScript = new ScriptStub('scriptId');
const app = new ApplicationStub().withAction(new CategoryStub(0).withScript(expectedScript)); const collection = new CategoryCollectionStub()
const sut = new ApplicationState(app); .withAction(new CategoryStub(0).withScript(expectedScript));
const sut = new CategoryCollectionState(collection);
// act // act
sut.selection.selectAll(); sut.selection.selectAll();
// assert // assert
@@ -59,20 +60,20 @@ describe('ApplicationState', () => {
describe('filter', () => { describe('filter', () => {
it('initialized with an empty filter', () => { it('initialized with an empty filter', () => {
// arrange // arrange
const app = new ApplicationStub(); const collection = new CategoryCollectionStub();
const sut = new ApplicationState(app); const sut = new CategoryCollectionState(collection);
// act // act
const actual = sut.filter.currentFilter; const actual = sut.filter.currentFilter;
// assert // assert
expect(actual).to.equal(undefined); expect(actual).to.equal(undefined);
}); });
it('can match a script from current application', () => { it('can match a script from current collection', () => {
// arrange // arrange
const scriptNameFilter = 'scriptName'; const scriptNameFilter = 'scriptName';
const expectedScript = new ScriptStub('scriptId').withName(scriptNameFilter); const expectedScript = new ScriptStub('scriptId').withName(scriptNameFilter);
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(0).withScript(expectedScript)); .withAction(new CategoryStub(0).withScript(expectedScript));
const sut = new ApplicationState(app); const sut = new CategoryCollectionState(collection);
// act // act
let actualScript: IScript; let actualScript: IScript;
sut.filter.filtered.on((result) => actualScript = result.scriptMatches[0]); sut.filter.filtered.on((result) => actualScript = result.scriptMatches[0]);

View File

@@ -1,8 +1,5 @@
import 'mocha'; import 'mocha';
import { expect } from 'chai'; import { expect } from 'chai';
import { CategoryStub } from '../../../../stubs/CategoryStub';
import { ScriptStub } from '../../../../stubs/ScriptStub';
import { ApplicationStub } from '../../../../stubs/ApplicationStub';
import { UserSelection } from '@/application/Context/State/Selection/UserSelection'; import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
import { ApplicationCode } from '@/application/Context/State/Code/ApplicationCode'; import { ApplicationCode } from '@/application/Context/State/Code/ApplicationCode';
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript'; import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
@@ -10,9 +7,12 @@ import { ICodeChangedEvent } from '@/application/Context/State/Code/Event/ICodeC
import { IUserScriptGenerator } from '@/application/Context/State/Code/Generation/IUserScriptGenerator'; import { IUserScriptGenerator } from '@/application/Context/State/Code/Generation/IUserScriptGenerator';
import { CodePosition } from '@/application/Context/State/Code/Position/CodePosition'; import { CodePosition } from '@/application/Context/State/Code/Position/CodePosition';
import { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition'; import { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
import { ScriptingDefinitionStub } from '../../../../stubs/ScriptingDefinitionStub';
import { IScriptingDefinition } from '@/domain/IScriptingDefinition'; import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
import { IUserScript } from '@/application/Context/State/Code/Generation/IUserScript'; import { IUserScript } from '@/application/Context/State/Code/Generation/IUserScript';
import { ScriptingDefinitionStub } from '../../../../stubs/ScriptingDefinitionStub';
import { CategoryStub } from '../../../../stubs/CategoryStub';
import { ScriptStub } from '../../../../stubs/ScriptStub';
import { CategoryCollectionStub } from '../../../../stubs/CategoryCollectionStub';
// TODO: Test scriptingDefinition: IScriptingDefinition logic // TODO: Test scriptingDefinition: IScriptingDefinition logic
@@ -20,7 +20,7 @@ describe('ApplicationCode', () => {
describe('ctor', () => { describe('ctor', () => {
it('empty when selection is empty', () => { it('empty when selection is empty', () => {
// arrange // arrange
const selection = new UserSelection(new ApplicationStub(), []); const selection = new UserSelection(new CategoryCollectionStub(), []);
const definition = new ScriptingDefinitionStub(); const definition = new ScriptingDefinitionStub();
const sut = new ApplicationCode(selection, definition); const sut = new ApplicationCode(selection, definition);
// act // act
@@ -31,8 +31,8 @@ describe('ApplicationCode', () => {
it('generates code from script generator when selection is not empty', () => { it('generates code from script generator when selection is not empty', () => {
// arrange // arrange
const scripts = [new ScriptStub('first'), new ScriptStub('second')]; const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const app = new ApplicationStub().withAction(new CategoryStub(1).withScripts(...scripts)); const collection = new CategoryCollectionStub().withAction(new CategoryStub(1).withScripts(...scripts));
const selection = new UserSelection(app, scripts.map((script) => script.toSelectedScript())); const selection = new UserSelection(collection, scripts.map((script) => script.toSelectedScript()));
const definition = new ScriptingDefinitionStub(); const definition = new ScriptingDefinitionStub();
const expected: IUserScript = { const expected: IUserScript = {
code: 'expected-code', code: 'expected-code',
@@ -53,8 +53,9 @@ describe('ApplicationCode', () => {
// arrange // arrange
let signaled: ICodeChangedEvent; let signaled: ICodeChangedEvent;
const scripts = [new ScriptStub('first'), new ScriptStub('second')]; const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const app = new ApplicationStub().withAction(new CategoryStub(1).withScripts(...scripts)); const collection = new CategoryCollectionStub().withAction(new CategoryStub(1).withScripts(...scripts));
const selection = new UserSelection(app, scripts.map((script) => new SelectedScript(script, false))); const scriptsToSelect = scripts.map((script) => new SelectedScript(script, false));
const selection = new UserSelection(collection, scriptsToSelect);
const definition = new ScriptingDefinitionStub(); const definition = new ScriptingDefinitionStub();
const sut = new ApplicationCode(selection, definition); const sut = new ApplicationCode(selection, definition);
sut.changed.on((code) => signaled = code); sut.changed.on((code) => signaled = code);
@@ -68,8 +69,9 @@ describe('ApplicationCode', () => {
// arrange // arrange
let signaled: ICodeChangedEvent; let signaled: ICodeChangedEvent;
const scripts = [new ScriptStub('first'), new ScriptStub('second')]; const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const app = new ApplicationStub().withAction(new CategoryStub(1).withScripts(...scripts)); const collection = new CategoryCollectionStub().withAction(new CategoryStub(1).withScripts(...scripts));
const selection = new UserSelection(app, scripts.map((script) => new SelectedScript(script, false))); const scriptsToSelect = scripts.map((script) => new SelectedScript(script, false));
const selection = new UserSelection(collection, scriptsToSelect);
const definition = new ScriptingDefinitionStub(); const definition = new ScriptingDefinitionStub();
const sut = new ApplicationCode(selection, definition); const sut = new ApplicationCode(selection, definition);
sut.changed.on((code) => signaled = code); sut.changed.on((code) => signaled = code);
@@ -84,8 +86,8 @@ describe('ApplicationCode', () => {
it('sends scripting definition to generator', () => { it('sends scripting definition to generator', () => {
// arrange // arrange
const expectedDefinition = new ScriptingDefinitionStub(); const expectedDefinition = new ScriptingDefinitionStub();
const app = new ApplicationStub(); const collection = new CategoryCollectionStub();
const selection = new UserSelection(app, []); const selection = new UserSelection(collection, []);
const generatorMock: IUserScriptGenerator = { const generatorMock: IUserScriptGenerator = {
buildCode: (selectedScripts, definition) => { buildCode: (selectedScripts, definition) => {
if (definition !== expectedDefinition) { if (definition !== expectedDefinition) {
@@ -108,9 +110,9 @@ describe('ApplicationCode', () => {
// arrange // arrange
const expectedDefinition = new ScriptingDefinitionStub(); const expectedDefinition = new ScriptingDefinitionStub();
const scripts = [new ScriptStub('first'), new ScriptStub('second')]; const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const app = new ApplicationStub().withAction(new CategoryStub(1).withScripts(...scripts)); const collection = new CategoryCollectionStub().withAction(new CategoryStub(1).withScripts(...scripts));
const selection = new UserSelection(app, scripts.map((script) => new SelectedScript(script, false))); const scriptsToSelect = scripts.map((script) => new SelectedScript(script, false));
const scriptsToSelect = scripts.map((s) => new SelectedScript(s, false)); const selection = new UserSelection(collection, scriptsToSelect);
const generatorMock: IUserScriptGenerator = { const generatorMock: IUserScriptGenerator = {
buildCode: (selectedScripts) => { buildCode: (selectedScripts) => {
if (JSON.stringify(selectedScripts) !== JSON.stringify(scriptsToSelect)) { if (JSON.stringify(selectedScripts) !== JSON.stringify(scriptsToSelect)) {
@@ -133,10 +135,11 @@ describe('ApplicationCode', () => {
// arrange // arrange
let signaled: ICodeChangedEvent; let signaled: ICodeChangedEvent;
const scripts = [new ScriptStub('first'), new ScriptStub('second')]; const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const app = new ApplicationStub().withAction(new CategoryStub(1).withScripts(...scripts)); const collection = new CategoryCollectionStub()
const selection = new UserSelection(app, scripts.map((script) => new SelectedScript(script, false))); .withAction(new CategoryStub(1).withScripts(...scripts));
const scriptsToSelect = scripts.map((script) => new SelectedScript(script, false));
const selection = new UserSelection(collection, scriptsToSelect);
const scriptingDefinition = new ScriptingDefinitionStub(); const scriptingDefinition = new ScriptingDefinitionStub();
const scriptsToSelect = scripts.map((s) => new SelectedScript(s, false));
const totalLines = 20; const totalLines = 20;
const expected = new Map<SelectedScript, ICodePosition>( const expected = new Map<SelectedScript, ICodePosition>(
[ [

View File

@@ -1,17 +1,18 @@
import { CategoryStub } from '../../../../stubs/CategoryStub';
import { ScriptStub } from '../../../../stubs/ScriptStub';
import { IFilterResult } from '@/application/Context/State/Filter/IFilterResult';
import { ApplicationStub } from '../../../../stubs/ApplicationStub';
import { UserFilter } from '@/application/Context/State/Filter/UserFilter';
import 'mocha'; import 'mocha';
import { expect } from 'chai'; import { expect } from 'chai';
import { IFilterResult } from '@/application/Context/State/Filter/IFilterResult';
import { UserFilter } from '@/application/Context/State/Filter/UserFilter';
import { CategoryStub } from '../../../../stubs/CategoryStub';
import { ScriptStub } from '../../../../stubs/ScriptStub';
import { CategoryCollectionStub } from '../../../../stubs/CategoryCollectionStub';
describe('UserFilter', () => { describe('UserFilter', () => {
describe('removeFilter', () => { describe('removeFilter', () => {
it('signals when removing filter', () => { it('signals when removing filter', () => {
// arrange // arrange
let isCalled = false; let isCalled = false;
const sut = new UserFilter(new ApplicationStub()); const sut = new UserFilter(new CategoryCollectionStub());
sut.filterRemoved.on(() => isCalled = true); sut.filterRemoved.on(() => isCalled = true);
// act // act
sut.removeFilter(); sut.removeFilter();
@@ -20,7 +21,7 @@ describe('UserFilter', () => {
}); });
it('sets currentFilter to undefined', () => { it('sets currentFilter to undefined', () => {
// arrange // arrange
const sut = new UserFilter(new ApplicationStub()); const sut = new UserFilter(new CategoryCollectionStub());
// act // act
sut.setFilter('non-important'); sut.setFilter('non-important');
sut.removeFilter(); sut.removeFilter();
@@ -33,7 +34,7 @@ describe('UserFilter', () => {
// arrange // arrange
let actual: IFilterResult; let actual: IFilterResult;
const nonMatchingFilter = 'non matching filter'; const nonMatchingFilter = 'non matching filter';
const sut = new UserFilter(new ApplicationStub()); const sut = new UserFilter(new CategoryCollectionStub());
sut.filtered.on((filterResult) => actual = filterResult); sut.filtered.on((filterResult) => actual = filterResult);
// act // act
sut.setFilter(nonMatchingFilter); sut.setFilter(nonMatchingFilter);
@@ -44,7 +45,7 @@ describe('UserFilter', () => {
it('sets currentFilter as expected when no matches', () => { it('sets currentFilter as expected when no matches', () => {
// arrange // arrange
const nonMatchingFilter = 'non matching filter'; const nonMatchingFilter = 'non matching filter';
const sut = new UserFilter(new ApplicationStub()); const sut = new UserFilter(new CategoryCollectionStub());
// act // act
sut.setFilter(nonMatchingFilter); sut.setFilter(nonMatchingFilter);
// assert // assert
@@ -61,7 +62,7 @@ describe('UserFilter', () => {
let actual: IFilterResult; let actual: IFilterResult;
const script = new ScriptStub('id').withCode(code); const script = new ScriptStub('id').withCode(code);
const category = new CategoryStub(33).withScript(script); const category = new CategoryStub(33).withScript(script);
const sut = new UserFilter(new ApplicationStub() const sut = new UserFilter(new CategoryCollectionStub()
.withAction(category)); .withAction(category));
sut.filtered.on((filterResult) => actual = filterResult); sut.filtered.on((filterResult) => actual = filterResult);
// act // act
@@ -81,7 +82,7 @@ describe('UserFilter', () => {
let actual: IFilterResult; let actual: IFilterResult;
const script = new ScriptStub('id').withRevertCode(revertCode); const script = new ScriptStub('id').withRevertCode(revertCode);
const category = new CategoryStub(33).withScript(script); const category = new CategoryStub(33).withScript(script);
const sut = new UserFilter(new ApplicationStub() const sut = new UserFilter(new CategoryCollectionStub()
.withAction(category)); .withAction(category));
sut.filtered.on((filterResult) => actual = filterResult); sut.filtered.on((filterResult) => actual = filterResult);
// act // act
@@ -101,7 +102,7 @@ describe('UserFilter', () => {
let actual: IFilterResult; let actual: IFilterResult;
const script = new ScriptStub('id').withName(name); const script = new ScriptStub('id').withName(name);
const category = new CategoryStub(33).withScript(script); const category = new CategoryStub(33).withScript(script);
const sut = new UserFilter(new ApplicationStub() const sut = new UserFilter(new CategoryCollectionStub()
.withAction(category)); .withAction(category));
sut.filtered.on((filterResult) => actual = filterResult); sut.filtered.on((filterResult) => actual = filterResult);
// act // act
@@ -121,7 +122,7 @@ describe('UserFilter', () => {
const filter = 'Hello WoRLD'; const filter = 'Hello WoRLD';
let actual: IFilterResult; let actual: IFilterResult;
const category = new CategoryStub(55).withName(categoryName); const category = new CategoryStub(55).withName(categoryName);
const sut = new UserFilter(new ApplicationStub() const sut = new UserFilter(new CategoryCollectionStub()
.withAction(category)); .withAction(category));
sut.filtered.on((filterResult) => actual = filterResult); sut.filtered.on((filterResult) => actual = filterResult);
// act // act
@@ -144,9 +145,9 @@ describe('UserFilter', () => {
const category = new CategoryStub(55) const category = new CategoryStub(55)
.withName(matchingText) .withName(matchingText)
.withScript(script); .withScript(script);
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(category); .withAction(category);
const sut = new UserFilter(app); const sut = new UserFilter(collection);
sut.filtered.on((filterResult) => actual = filterResult); sut.filtered.on((filterResult) => actual = filterResult);
// act // act
sut.setFilter(filter); sut.setFilter(filter);

View File

@@ -1,21 +1,21 @@
import 'mocha'; import 'mocha';
import { expect } from 'chai'; import { expect } from 'chai';
import { IScript } from '@/domain/IScript'; import { IScript } from '@/domain/IScript';
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
import { CategoryStub } from '../../../../stubs/CategoryStub';
import { CategoryCollectionStub } from '../../../../stubs/CategoryCollectionStub';
import { SelectedScriptStub } from '../../../../stubs/SelectedScriptStub'; import { SelectedScriptStub } from '../../../../stubs/SelectedScriptStub';
import { ScriptStub } from '../../../../stubs/ScriptStub'; import { ScriptStub } from '../../../../stubs/ScriptStub';
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
import { CategoryStub } from '../../../../stubs/CategoryStub';
import { ApplicationStub } from '../../../../stubs/ApplicationStub';
import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
describe('UserSelection', () => { describe('UserSelection', () => {
describe('ctor', () => { describe('ctor', () => {
it('has nothing with no initial selection', () => { it('has nothing with no initial selection', () => {
// arrange // arrange
const app = new ApplicationStub().withAction(new CategoryStub(1).withScriptIds('s1')); const collection = new CategoryCollectionStub().withAction(new CategoryStub(1).withScriptIds('s1'));
const selection = []; const selection = [];
// act // act
const sut = new UserSelection(app, selection); const sut = new UserSelection(collection, selection);
// assert // assert
expect(sut.selectedScripts).to.have.lengthOf(0); expect(sut.selectedScripts).to.have.lengthOf(0);
}); });
@@ -23,11 +23,11 @@ describe('UserSelection', () => {
// arrange // arrange
const firstScript = new ScriptStub('1'); const firstScript = new ScriptStub('1');
const secondScript = new ScriptStub('2'); const secondScript = new ScriptStub('2');
const app = new ApplicationStub().withAction( const collection = new CategoryCollectionStub()
new CategoryStub(1).withScript(firstScript).withScripts(secondScript)); .withAction(new CategoryStub(1).withScript(firstScript).withScripts(secondScript));
const expected = [ new SelectedScript(firstScript, false), new SelectedScript(secondScript, true) ]; const expected = [ new SelectedScript(firstScript, false), new SelectedScript(secondScript, true) ];
// act // act
const sut = new UserSelection(app, expected); const sut = new UserSelection(collection, expected);
// assert // assert
expect(sut.selectedScripts).to.deep.include(expected[0]); expect(sut.selectedScripts).to.deep.include(expected[0]);
expect(sut.selectedScripts).to.deep.include(expected[1]); expect(sut.selectedScripts).to.deep.include(expected[1]);
@@ -36,13 +36,13 @@ describe('UserSelection', () => {
it('deselectAll removes all items', () => { it('deselectAll removes all items', () => {
// arrange // arrange
const events: Array<readonly SelectedScript[]> = []; const events: Array<readonly SelectedScript[]> = [];
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1) .withAction(new CategoryStub(1)
.withScriptIds('s1', 's2', 's3', 's4')); .withScriptIds('s1', 's2', 's3', 's4'));
const selectedScripts = [ const selectedScripts = [
new SelectedScriptStub('s1'), new SelectedScriptStub('s2'), new SelectedScriptStub('s3'), new SelectedScriptStub('s1'), new SelectedScriptStub('s2'), new SelectedScriptStub('s3'),
]; ];
const sut = new UserSelection(app, selectedScripts); const sut = new UserSelection(collection, selectedScripts);
sut.changed.on((newScripts) => events.push(newScripts)); sut.changed.on((newScripts) => events.push(newScripts));
// act // act
sut.deselectAll(); sut.deselectAll();
@@ -54,13 +54,13 @@ describe('UserSelection', () => {
it('selectOnly selects expected', () => { it('selectOnly selects expected', () => {
// arrange // arrange
const events: Array<readonly SelectedScript[]> = []; const events: Array<readonly SelectedScript[]> = [];
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1) .withAction(new CategoryStub(1)
.withScriptIds('s1', 's2', 's3', 's4')); .withScriptIds('s1', 's2', 's3', 's4'));
const selectedScripts = [ const selectedScripts = [
new SelectedScriptStub('s1'), new SelectedScriptStub('s2'), new SelectedScriptStub('s3'), new SelectedScriptStub('s1'), new SelectedScriptStub('s2'), new SelectedScriptStub('s3'),
]; ];
const sut = new UserSelection(app, selectedScripts); const sut = new UserSelection(collection, selectedScripts);
sut.changed.on((newScripts) => events.push(newScripts)); sut.changed.on((newScripts) => events.push(newScripts));
const scripts = [new ScriptStub('s2'), new ScriptStub('s3'), new ScriptStub('s4')]; const scripts = [new ScriptStub('s2'), new ScriptStub('s3'), new ScriptStub('s4')];
const expected = [ new SelectedScriptStub('s2'), new SelectedScriptStub('s3'), const expected = [ new SelectedScriptStub('s2'), new SelectedScriptStub('s3'),
@@ -78,10 +78,10 @@ describe('UserSelection', () => {
// arrange // arrange
const events: Array<readonly SelectedScript[]> = []; const events: Array<readonly SelectedScript[]> = [];
const scripts: IScript[] = [new ScriptStub('s1'), new ScriptStub('s2'), new ScriptStub('s3'), new ScriptStub('s4')]; const scripts: IScript[] = [new ScriptStub('s1'), new ScriptStub('s2'), new ScriptStub('s3'), new ScriptStub('s4')];
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1) .withAction(new CategoryStub(1)
.withScripts(...scripts)); .withScripts(...scripts));
const sut = new UserSelection(app, []); const sut = new UserSelection(collection, []);
sut.changed.on((newScripts) => events.push(newScripts)); sut.changed.on((newScripts) => events.push(newScripts));
const expected = scripts.map((script) => new SelectedScript(script, false)); const expected = scripts.map((script) => new SelectedScript(script, false));
// act // act
@@ -95,10 +95,10 @@ describe('UserSelection', () => {
it('adds when item does not exist', () => { it('adds when item does not exist', () => {
// arrange // arrange
const events: Array<readonly SelectedScript[]> = []; const events: Array<readonly SelectedScript[]> = [];
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1) .withAction(new CategoryStub(1)
.withScripts(new ScriptStub('s1'), new ScriptStub('s2'))); .withScripts(new ScriptStub('s1'), new ScriptStub('s2')));
const sut = new UserSelection(app, []); const sut = new UserSelection(collection, []);
sut.changed.on((scripts) => events.push(scripts)); sut.changed.on((scripts) => events.push(scripts));
const expected = [ new SelectedScript(new ScriptStub('s1'), false) ]; const expected = [ new SelectedScript(new ScriptStub('s1'), false) ];
// act // act
@@ -111,10 +111,10 @@ describe('UserSelection', () => {
it('updates when item exists', () => { it('updates when item exists', () => {
// arrange // arrange
const events: Array<readonly SelectedScript[]> = []; const events: Array<readonly SelectedScript[]> = [];
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1) .withAction(new CategoryStub(1)
.withScripts(new ScriptStub('s1'), new ScriptStub('s2'))); .withScripts(new ScriptStub('s1'), new ScriptStub('s2')));
const sut = new UserSelection(app, []); const sut = new UserSelection(collection, []);
sut.changed.on((scripts) => events.push(scripts)); sut.changed.on((scripts) => events.push(scripts));
const expected = [ new SelectedScript(new ScriptStub('s1'), true) ]; const expected = [ new SelectedScript(new ScriptStub('s1'), true) ];
// act // act
@@ -130,10 +130,10 @@ describe('UserSelection', () => {
// arrange // arrange
const events: Array<readonly SelectedScript[]> = []; const events: Array<readonly SelectedScript[]> = [];
const categoryId = 1; const categoryId = 1;
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(categoryId) .withAction(new CategoryStub(categoryId)
.withScripts(new ScriptStub('s1'), new ScriptStub('s2'))); .withScripts(new ScriptStub('s1'), new ScriptStub('s2')));
const sut = new UserSelection(app, []); const sut = new UserSelection(collection, []);
sut.changed.on((s) => events.push(s)); sut.changed.on((s) => events.push(s));
// act // act
sut.removeAllInCategory(categoryId); sut.removeAllInCategory(categoryId);
@@ -145,10 +145,10 @@ describe('UserSelection', () => {
// arrange // arrange
const categoryId = 1; const categoryId = 1;
const scripts = [new SelectedScriptStub('s1'), new SelectedScriptStub('s2')]; const scripts = [new SelectedScriptStub('s1'), new SelectedScriptStub('s2')];
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(categoryId) .withAction(new CategoryStub(categoryId)
.withScripts(...scripts.map((script) => script.script))); .withScripts(...scripts.map((script) => script.script)));
const sut = new UserSelection(app, scripts); const sut = new UserSelection(collection, scripts);
// act // act
sut.removeAllInCategory(categoryId); sut.removeAllInCategory(categoryId);
// assert // assert
@@ -160,10 +160,10 @@ describe('UserSelection', () => {
const categoryId = 1; const categoryId = 1;
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 app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(categoryId) .withAction(new CategoryStub(categoryId)
.withScripts(...existing, ...notExisting)); .withScripts(...existing, ...notExisting));
const sut = new UserSelection(app, existing.map((script) => new SelectedScript(script, false))); const sut = new UserSelection(collection, existing.map((script) => new SelectedScript(script, false)));
// act // act
sut.removeAllInCategory(categoryId); sut.removeAllInCategory(categoryId);
// assert // assert
@@ -177,10 +177,10 @@ describe('UserSelection', () => {
const events: Array<readonly SelectedScript[]> = []; const events: Array<readonly SelectedScript[]> = [];
const categoryId = 1; const categoryId = 1;
const scripts = [new ScriptStub('s1'), new ScriptStub('s2')]; const scripts = [new ScriptStub('s1'), new ScriptStub('s2')];
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(categoryId) .withAction(new CategoryStub(categoryId)
.withScripts(...scripts)); .withScripts(...scripts));
const sut = new UserSelection(app, scripts.map((script) => new SelectedScript(script, false))); const sut = new UserSelection(collection, scripts.map((script) => new SelectedScript(script, false)));
sut.changed.on((s) => events.push(s)); sut.changed.on((s) => events.push(s));
// act // act
sut.addOrUpdateAllInCategory(categoryId); sut.addOrUpdateAllInCategory(categoryId);
@@ -193,10 +193,10 @@ describe('UserSelection', () => {
// arrange // arrange
const categoryId = 1; const categoryId = 1;
const expected = [new ScriptStub('s1'), new ScriptStub('s2')]; const expected = [new ScriptStub('s1'), new ScriptStub('s2')];
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(categoryId) .withAction(new CategoryStub(categoryId)
.withScripts(...expected)); .withScripts(...expected));
const sut = new UserSelection(app, []); const sut = new UserSelection(collection, []);
// act // act
sut.addOrUpdateAllInCategory(categoryId); sut.addOrUpdateAllInCategory(categoryId);
// assert // assert
@@ -207,10 +207,10 @@ describe('UserSelection', () => {
// arrange // arrange
const categoryId = 1; const categoryId = 1;
const expected = [new ScriptStub('s1'), new ScriptStub('s2')]; const expected = [new ScriptStub('s1'), new ScriptStub('s2')];
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(categoryId) .withAction(new CategoryStub(categoryId)
.withScripts(...expected)); .withScripts(...expected));
const sut = new UserSelection(app, []); const sut = new UserSelection(collection, []);
// act // act
sut.addOrUpdateAllInCategory(categoryId, true); sut.addOrUpdateAllInCategory(categoryId, true);
// assert // assert
@@ -223,10 +223,10 @@ describe('UserSelection', () => {
const notExisting = [ new ScriptStub('notExisting1'), new ScriptStub('notExisting2') ]; const notExisting = [ new ScriptStub('notExisting1'), new ScriptStub('notExisting2') ];
const existing = [ new ScriptStub('existing1'), new ScriptStub('existing2') ]; const existing = [ new ScriptStub('existing1'), new ScriptStub('existing2') ];
const allScripts = [ ...existing, ...notExisting ]; const allScripts = [ ...existing, ...notExisting ];
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(categoryId) .withAction(new CategoryStub(categoryId)
.withScripts(...allScripts)); .withScripts(...allScripts));
const sut = new UserSelection(app, existing.map((script) => new SelectedScript(script, false))); const sut = new UserSelection(collection, existing.map((script) => new SelectedScript(script, false)));
// act // act
sut.addOrUpdateAllInCategory(categoryId, true); sut.addOrUpdateAllInCategory(categoryId, true);
// assert // assert
@@ -239,10 +239,10 @@ describe('UserSelection', () => {
const notExisting = [ new ScriptStub('notExisting1'), new ScriptStub('notExisting2') ]; const notExisting = [ new ScriptStub('notExisting1'), new ScriptStub('notExisting2') ];
const existing = [ new ScriptStub('existing1'), new ScriptStub('existing2') ]; const existing = [ new ScriptStub('existing1'), new ScriptStub('existing2') ];
const allScripts = [ ...existing, ...notExisting ]; const allScripts = [ ...existing, ...notExisting ];
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(categoryId) .withAction(new CategoryStub(categoryId)
.withScripts(...allScripts)); .withScripts(...allScripts));
const sut = new UserSelection(app, existing.map((script) => new SelectedScript(script, false))); const sut = new UserSelection(collection, existing.map((script) => new SelectedScript(script, false)));
// act // act
sut.addOrUpdateAllInCategory(categoryId, true); sut.addOrUpdateAllInCategory(categoryId, true);
// assert // assert
@@ -253,10 +253,10 @@ describe('UserSelection', () => {
// arrange // arrange
const categoryId = 1; const categoryId = 1;
const scripts = [ new ScriptStub('existing1'), new ScriptStub('existing2') ]; const scripts = [ new ScriptStub('existing1'), new ScriptStub('existing2') ];
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(categoryId) .withAction(new CategoryStub(categoryId)
.withScripts(...scripts)); .withScripts(...scripts));
const sut = new UserSelection(app, scripts.map((script) => new SelectedScript(script, false))); const sut = new UserSelection(collection, scripts.map((script) => new SelectedScript(script, false)));
// act // act
sut.addOrUpdateAllInCategory(categoryId, true); sut.addOrUpdateAllInCategory(categoryId, true);
// assert // assert
@@ -269,10 +269,10 @@ describe('UserSelection', () => {
// arrange // arrange
const selectedScript = new ScriptStub('selected'); const selectedScript = new ScriptStub('selected');
const notSelectedScript = new ScriptStub('not selected'); const notSelectedScript = new ScriptStub('not selected');
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1) .withAction(new CategoryStub(1)
.withScripts(selectedScript, notSelectedScript)); .withScripts(selectedScript, notSelectedScript));
const sut = new UserSelection(app, [ new SelectedScript(selectedScript, false) ]); const sut = new UserSelection(collection, [ new SelectedScript(selectedScript, false) ]);
// act // act
const actual = sut.isSelected(notSelectedScript.id); const actual = sut.isSelected(notSelectedScript.id);
// assert // assert
@@ -282,10 +282,10 @@ describe('UserSelection', () => {
// arrange // arrange
const selectedScript = new ScriptStub('selected'); const selectedScript = new ScriptStub('selected');
const notSelectedScript = new ScriptStub('not selected'); const notSelectedScript = new ScriptStub('not selected');
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1) .withAction(new CategoryStub(1)
.withScripts(selectedScript, notSelectedScript)); .withScripts(selectedScript, notSelectedScript));
const sut = new UserSelection(app, [ new SelectedScript(selectedScript, false) ]); const sut = new UserSelection(collection, [ new SelectedScript(selectedScript, false) ]);
// act // act
const actual = sut.isSelected(selectedScript.id); const actual = sut.isSelected(selectedScript.id);
// assert // assert
@@ -297,8 +297,8 @@ describe('UserSelection', () => {
// arrange // arrange
const category = new CategoryStub(1) const category = new CategoryStub(1)
.withScriptIds('non-selected-script-1', 'non-selected-script-2'); .withScriptIds('non-selected-script-1', 'non-selected-script-2');
const app = new ApplicationStub().withAction(category); const collection = new CategoryCollectionStub().withAction(category);
const sut = new UserSelection(app, [ ]); const sut = new UserSelection(collection, [ ]);
it('areAllSelected returns false', () => { it('areAllSelected returns false', () => {
// act // act
const actual = sut.areAllSelected(category); const actual = sut.areAllSelected(category);
@@ -317,10 +317,10 @@ describe('UserSelection', () => {
const category = new CategoryStub(1) const category = new CategoryStub(1)
.withScriptIds('non-selected-script-1', 'non-selected-script-2'); .withScriptIds('non-selected-script-1', 'non-selected-script-2');
const selectedScript = new ScriptStub('selected'); const selectedScript = new ScriptStub('selected');
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(category) .withAction(category)
.withAction(new CategoryStub(22).withScript(selectedScript)); .withAction(new CategoryStub(22).withScript(selectedScript));
const sut = new UserSelection(app, [ new SelectedScript(selectedScript, false) ]); const sut = new UserSelection(collection, [ new SelectedScript(selectedScript, false) ]);
it('areAllSelected returns false', () => { it('areAllSelected returns false', () => {
// act // act
const actual = sut.areAllSelected(category); const actual = sut.areAllSelected(category);
@@ -340,8 +340,8 @@ describe('UserSelection', () => {
const category = new CategoryStub(1) const category = new CategoryStub(1)
.withScriptIds('non-selected-script-1', 'non-selected-script-2') .withScriptIds('non-selected-script-1', 'non-selected-script-2')
.withCategory(new CategoryStub(12).withScript(selectedScript)); .withCategory(new CategoryStub(12).withScript(selectedScript));
const app = new ApplicationStub().withAction(category); const collection = new CategoryCollectionStub().withAction(category);
const sut = new UserSelection(app, [ new SelectedScript(selectedScript, false) ]); const sut = new UserSelection(collection, [ new SelectedScript(selectedScript, false) ]);
it('areAllSelected returns false', () => { it('areAllSelected returns false', () => {
// act // act
const actual = sut.areAllSelected(category); const actual = sut.areAllSelected(category);
@@ -362,8 +362,8 @@ describe('UserSelection', () => {
const category = new CategoryStub(1) const category = new CategoryStub(1)
.withScript(firstSelectedScript) .withScript(firstSelectedScript)
.withCategory(new CategoryStub(12).withScript(secondSelectedScript)); .withCategory(new CategoryStub(12).withScript(secondSelectedScript));
const app = new ApplicationStub().withAction(category); const collection = new CategoryCollectionStub().withAction(category);
const sut = new UserSelection(app, const sut = new UserSelection(collection,
[ firstSelectedScript, secondSelectedScript ].map((s) => new SelectedScript(s, false))); [ firstSelectedScript, secondSelectedScript ].map((s) => new SelectedScript(s, false)));
it('areAllSelected returns true', () => { it('areAllSelected returns true', () => {
// act // act
@@ -378,6 +378,5 @@ describe('UserSelection', () => {
expect(actual).to.equal(true); expect(actual).to.equal(true);
}); });
}); });
}); });
}); });

View File

@@ -1,6 +1,6 @@
import { IEntity } from '@/infrastructure/Entity/IEntity'; import { IEntity } from '@/infrastructure/Entity/IEntity';
import applicationFile, { YamlCategory, YamlScript, YamlApplication, YamlScriptingDefinition } from 'js-yaml-loader!@/application/application.yaml'; import applicationFile, { YamlCategory, YamlScript, YamlApplication, YamlScriptingDefinition } from 'js-yaml-loader!@/application/application.yaml';
import { parseApplication } from '@/application/Parser/ApplicationParser'; import { parseCategoryCollection } from '@/application/Parser/CategoryCollectionParser';
import 'mocha'; import 'mocha';
import { expect } from 'chai'; import { expect } from 'chai';
import { parseCategory } from '@/application/Parser/CategoryParser'; import { parseCategory } from '@/application/Parser/CategoryParser';
@@ -12,47 +12,49 @@ import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
import { parseScriptingDefinition } from '@/application/Parser/ScriptingDefinitionParser'; import { parseScriptingDefinition } from '@/application/Parser/ScriptingDefinitionParser';
import { mockEnumParser } from '../../stubs/EnumParserStub'; import { mockEnumParser } from '../../stubs/EnumParserStub';
describe('ApplicationParser', () => { describe('CategoryCollectionParser', () => {
describe('parseApplication', () => { describe('parseCategoryCollection', () => {
it('can parse current application file', () => { it('can parse current application file', () => {
// act // act
const act = () => parseApplication(applicationFile); const act = () => parseCategoryCollection(applicationFile);
// assert // assert
expect(act).to.not.throw(); expect(act).to.not.throw();
}); });
it('throws when undefined', () => { it('throws when undefined', () => {
// arrange // arrange
const expectedError = 'application is null or undefined'; const expectedError = 'content is null or undefined';
// act // act
const act = () => parseApplication(undefined); const act = () => parseCategoryCollection(undefined);
// assert // assert
expect(act).to.throw(expectedError); expect(act).to.throw(expectedError);
}); });
describe('actions', () => { describe('actions', () => {
it('throws when undefined actions', () => { it('throws when undefined actions', () => {
// arrange // arrange
const app = new YamlApplicationBuilder().withActions(undefined).build(); const expectedError = 'content does not define any action';
const collection = new YamlApplicationBuilder().withActions(undefined).build();
// act // act
const act = () => parseApplication(app); const act = () => parseCategoryCollection(collection);
// assert // assert
expect(act).to.throw('application does not define any action'); expect(act).to.throw(expectedError);
}); });
it('throws when has no actions', () => { it('throws when has no actions', () => {
// arrange // arrange
const app = new YamlApplicationBuilder().withActions([]).build(); const expectedError = 'content does not define any action';
const collection = new YamlApplicationBuilder().withActions([]).build();
// act // act
const act = () => parseApplication(app); const act = () => parseCategoryCollection(collection);
// assert // assert
expect(act).to.throw('application does not define any action'); expect(act).to.throw(expectedError);
}); });
it('parses actions', () => { it('parses actions', () => {
// arrange // arrange
const actions = [ getTestCategory('test1'), getTestCategory('test2') ]; const actions = [ getTestCategory('test1'), getTestCategory('test2') ];
const compiler = new ScriptCompilerStub(); const compiler = new ScriptCompilerStub();
const expected = [ parseCategory(actions[0], compiler), parseCategory(actions[1], compiler) ]; const expected = [ parseCategory(actions[0], compiler), parseCategory(actions[1], compiler) ];
const app = new YamlApplicationBuilder().withActions(actions).build(); const collection = new YamlApplicationBuilder().withActions(actions).build();
// act // act
const actual = parseApplication(app).actions; const actual = parseCategoryCollection(collection).actions;
// assert // assert
expect(excludingId(actual)).to.be.deep.equal(excludingId(expected)); expect(excludingId(actual)).to.be.deep.equal(excludingId(expected));
function excludingId<TId>(array: ReadonlyArray<IEntity<TId>>) { function excludingId<TId>(array: ReadonlyArray<IEntity<TId>>) {
@@ -69,9 +71,9 @@ describe('ApplicationParser', () => {
const expected = 'expected-version'; const expected = 'expected-version';
const env = getProcessEnvironmentStub(); const env = getProcessEnvironmentStub();
env.VUE_APP_VERSION = expected; env.VUE_APP_VERSION = expected;
const app = new YamlApplicationBuilder().build(); const collection = new YamlApplicationBuilder().build();
// act // act
const actual = parseApplication(app, env).info.version; const actual = parseCategoryCollection(collection, env).info.version;
// assert // assert
expect(actual).to.be.equal(expected); expect(actual).to.be.equal(expected);
}); });
@@ -80,9 +82,9 @@ describe('ApplicationParser', () => {
const expected = 'https://expected-repository.url'; const expected = 'https://expected-repository.url';
const env = getProcessEnvironmentStub(); const env = getProcessEnvironmentStub();
env.VUE_APP_REPOSITORY_URL = expected; env.VUE_APP_REPOSITORY_URL = expected;
const app = new YamlApplicationBuilder().build(); const collection = new YamlApplicationBuilder().build();
// act // act
const actual = parseApplication(app, env).info.repositoryUrl; const actual = parseCategoryCollection(collection, env).info.repositoryUrl;
// assert // assert
expect(actual).to.be.equal(expected); expect(actual).to.be.equal(expected);
}); });
@@ -91,9 +93,9 @@ describe('ApplicationParser', () => {
const expected = 'expected-app-name'; const expected = 'expected-app-name';
const env = getProcessEnvironmentStub(); const env = getProcessEnvironmentStub();
env.VUE_APP_NAME = expected; env.VUE_APP_NAME = expected;
const app = new YamlApplicationBuilder().build(); const collection = new YamlApplicationBuilder().build();
// act // act
const actual = parseApplication(app, env).info.name; const actual = parseCategoryCollection(collection, env).info.name;
// assert // assert
expect(actual).to.be.equal(expected); expect(actual).to.be.equal(expected);
}); });
@@ -102,9 +104,9 @@ describe('ApplicationParser', () => {
const expected = 'https://expected.sexy'; const expected = 'https://expected.sexy';
const env = getProcessEnvironmentStub(); const env = getProcessEnvironmentStub();
env.VUE_APP_HOMEPAGE_URL = expected; env.VUE_APP_HOMEPAGE_URL = expected;
const app = new YamlApplicationBuilder().build(); const collection = new YamlApplicationBuilder().build();
// act // act
const actual = parseApplication(app, env).info.homepage; const actual = parseCategoryCollection(collection, env).info.homepage;
// assert // assert
expect(actual).to.be.equal(expected); expect(actual).to.be.equal(expected);
}); });
@@ -112,11 +114,11 @@ describe('ApplicationParser', () => {
describe('scripting definition', () => { describe('scripting definition', () => {
it('parses scripting definition as expected', () => { it('parses scripting definition as expected', () => {
// arrange // arrange
const app = new YamlApplicationBuilder().build(); const collection = new YamlApplicationBuilder().build();
const information = parseProjectInformation(process.env); const information = parseProjectInformation(process.env);
const expected = parseScriptingDefinition(app.scripting, information); const expected = parseScriptingDefinition(collection.scripting, information);
// act // act
const actual = parseApplication(app).scripting; const actual = parseCategoryCollection(collection).scripting;
// assert // assert
expect(expected).to.deep.equal(actual); expect(expected).to.deep.equal(actual);
}); });
@@ -127,13 +129,13 @@ describe('ApplicationParser', () => {
const expectedOs = OperatingSystem.macOS; const expectedOs = OperatingSystem.macOS;
const osText = 'macos'; const osText = 'macos';
const expectedName = 'os'; const expectedName = 'os';
const app = new YamlApplicationBuilder() const collection = new YamlApplicationBuilder()
.withOs(osText) .withOs(osText)
.build(); .build();
const parserMock = mockEnumParser(expectedName, osText, expectedOs); const parserMock = mockEnumParser(expectedName, osText, expectedOs);
const env = getProcessEnvironmentStub(); const env = getProcessEnvironmentStub();
// act // act
const actual = parseApplication(app, env, parserMock); const actual = parseCategoryCollection(collection, env, parserMock);
// assert // assert
expect(actual.os).to.equal(expectedOs); expect(actual.os).to.equal(expectedOs);
}); });

View File

@@ -1,18 +1,18 @@
import { ScriptStub } from './../stubs/ScriptStub'; import { ScriptStub } from '../stubs/ScriptStub';
import { CategoryStub } from './../stubs/CategoryStub'; import { CategoryStub } from '../stubs/CategoryStub';
import { Application } from '@/domain/Application';
import 'mocha'; import 'mocha';
import { expect } from 'chai'; import { expect } from 'chai';
import { ProjectInformation } from '@/domain/ProjectInformation'; import { ProjectInformation } from '@/domain/ProjectInformation';
import { IProjectInformation } from '@/domain/IProjectInformation'; import { IProjectInformation } from '@/domain/IProjectInformation';
import { ICategory } from '@/domain/IApplication'; import { ICategory } from '@/domain/ICategory';
import { OperatingSystem } from '@/domain/OperatingSystem'; import { OperatingSystem } from '@/domain/OperatingSystem';
import { IScriptingDefinition } from '@/domain/IScriptingDefinition'; import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
import { ScriptingLanguage } from '@/domain/ScriptingLanguage'; import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
import { RecommendationLevel } from '@/domain/RecommendationLevel'; import { RecommendationLevel } from '@/domain/RecommendationLevel';
import { getEnumValues } from '@/application/Common/Enum'; import { getEnumValues } from '@/application/Common/Enum';
import { CategoryCollection } from '../../../src/domain/CategoryCollection';
describe('Application', () => { describe('CategoryCollection', () => {
describe('getScriptsByLevel', () => { describe('getScriptsByLevel', () => {
it('filters out scripts without levels', () => { it('filters out scripts without levels', () => {
// arrange // arrange
@@ -25,7 +25,9 @@ describe('Application', () => {
const category = new CategoryStub(0) const category = new CategoryStub(0)
.withScripts(...scriptsWithLevels) .withScripts(...scriptsWithLevels)
.withScript(toIgnore); .withScript(toIgnore);
const sut = new ApplicationBuilder().withActions([category]).construct(); const sut = new CategoryCollectionBuilder()
.withActions([category])
.construct();
// act // act
const actual = sut.getScriptsByLevel(currentLevel); const actual = sut.getScriptsByLevel(currentLevel);
// assert // assert
@@ -43,7 +45,9 @@ describe('Application', () => {
new CategoryStub(3).withScripts(...expected, new CategoryStub(3).withScripts(...expected,
new ScriptStub('S3').withLevel(RecommendationLevel.Strict)), new ScriptStub('S3').withLevel(RecommendationLevel.Strict)),
]; ];
const sut = new ApplicationBuilder().withActions(actions).construct(); const sut = new CategoryCollectionBuilder()
.withActions(actions)
.construct();
// act // act
const actual = sut.getScriptsByLevel(level); const actual = sut.getScriptsByLevel(level);
// assert // assert
@@ -59,7 +63,9 @@ describe('Application', () => {
const actions = [ const actions = [
new CategoryStub(3).withScripts(...expected), new CategoryStub(3).withScripts(...expected),
]; ];
const sut = new ApplicationBuilder().withActions(actions).construct(); const sut = new CategoryCollectionBuilder()
.withActions(actions)
.construct();
// act // act
const actual = sut.getScriptsByLevel(level); const actual = sut.getScriptsByLevel(level);
// assert // assert
@@ -67,7 +73,8 @@ describe('Application', () => {
}); });
it('throws when level is undefined', () => { it('throws when level is undefined', () => {
// arrange // arrange
const sut = new ApplicationBuilder().construct(); const sut = new CategoryCollectionBuilder()
.construct();
// act // act
const act = () => sut.getScriptsByLevel(undefined); const act = () => sut.getScriptsByLevel(undefined);
// assert // assert
@@ -76,7 +83,8 @@ describe('Application', () => {
it('throws when level is out of range', () => { it('throws when level is out of range', () => {
// arrange // arrange
const invalidValue = 66; const invalidValue = 66;
const sut = new ApplicationBuilder().construct(); const sut = new CategoryCollectionBuilder()
.construct();
// act // act
const act = () => sut.getScriptsByLevel(invalidValue); const act = () => sut.getScriptsByLevel(invalidValue);
// assert // assert
@@ -89,10 +97,12 @@ describe('Application', () => {
const categories = []; const categories = [];
// act // act
function construct() { function construct() {
new ApplicationBuilder().withActions(categories).construct(); new CategoryCollectionBuilder()
.withActions(categories)
.construct();
} }
// assert // assert
expect(construct).to.throw('Application must consist of at least one category'); expect(construct).to.throw('must consist of at least one category');
}); });
it('cannot construct without scripts', () => { it('cannot construct without scripts', () => {
// arrange // arrange
@@ -102,10 +112,12 @@ describe('Application', () => {
]; ];
// act // act
function construct() { function construct() {
new ApplicationBuilder().withActions(categories).construct(); new CategoryCollectionBuilder()
.withActions(categories)
.construct();
} }
// assert // assert
expect(construct).to.throw('Application must consist of at least one script'); expect(construct).to.throw('must consist of at least one script');
}); });
describe('cannot construct without any recommended scripts', () => { describe('cannot construct without any recommended scripts', () => {
// arrange // arrange
@@ -119,7 +131,7 @@ describe('Application', () => {
new ScriptStub(`Script${index}`).withLevel(level), new ScriptStub(`Script${index}`).withLevel(level),
)); ));
// act // act
const construct = () => new ApplicationBuilder() const construct = () => new CategoryCollectionBuilder()
.withActions(categories) .withActions(categories)
.construct(); .construct();
// assert // assert
@@ -141,7 +153,9 @@ describe('Application', () => {
new CategoryStub(4).withScripts(new ScriptStub('S4'))), new CategoryStub(4).withScripts(new ScriptStub('S4'))),
]; ];
// act // act
const sut = new ApplicationBuilder().withActions(categories).construct(); const sut = new CategoryCollectionBuilder()
.withActions(categories)
.construct();
// assert // assert
expect(sut.totalScripts).to.equal(4); expect(sut.totalScripts).to.equal(4);
}); });
@@ -156,7 +170,7 @@ describe('Application', () => {
new CategoryStub(3).withCategories(new CategoryStub(4).withScripts(new ScriptStub('S4'))), new CategoryStub(3).withCategories(new CategoryStub(4).withScripts(new ScriptStub('S4'))),
]; ];
// act // act
const sut = new ApplicationBuilder() const sut = new CategoryCollectionBuilder()
.withActions(categories) .withActions(categories)
.construct(); .construct();
// assert // assert
@@ -169,7 +183,9 @@ describe('Application', () => {
const expected = new ProjectInformation( const expected = new ProjectInformation(
'expected-name', 'expected-repo', '0.31.0', 'expected-homepage'); 'expected-name', 'expected-repo', '0.31.0', 'expected-homepage');
// act // act
const sut = new ApplicationBuilder().withInfo(expected).construct(); const sut = new CategoryCollectionBuilder()
.withInfo(expected)
.construct();
// assert // assert
expect(sut.info).to.deep.equal(expected); expect(sut.info).to.deep.equal(expected);
}); });
@@ -178,7 +194,9 @@ describe('Application', () => {
const information = undefined; const information = undefined;
// act // act
function construct() { function construct() {
return new ApplicationBuilder().withInfo(information).construct(); return new CategoryCollectionBuilder()
.withInfo(information)
.construct();
} }
// assert // assert
expect(construct).to.throw('undefined info'); expect(construct).to.throw('undefined info');
@@ -189,7 +207,9 @@ describe('Application', () => {
// arrange // arrange
const expected = OperatingSystem.macOS; const expected = OperatingSystem.macOS;
// act // act
const sut = new ApplicationBuilder().withOs(expected).construct(); const sut = new CategoryCollectionBuilder()
.withOs(expected)
.construct();
// assert // assert
expect(sut.os).to.deep.equal(expected); expect(sut.os).to.deep.equal(expected);
}); });
@@ -197,7 +217,9 @@ describe('Application', () => {
// arrange // arrange
const os = OperatingSystem.Unknown; const os = OperatingSystem.Unknown;
// act // act
const construct = () => new ApplicationBuilder().withOs(os).construct(); const construct = () => new CategoryCollectionBuilder()
.withOs(os)
.construct();
// assert // assert
expect(construct).to.throw('unknown os'); expect(construct).to.throw('unknown os');
}); });
@@ -205,7 +227,9 @@ describe('Application', () => {
// arrange // arrange
const os = undefined; const os = undefined;
// act // act
const construct = () => new ApplicationBuilder().withOs(os).construct(); const construct = () => new CategoryCollectionBuilder()
.withOs(os)
.construct();
// assert // assert
expect(construct).to.throw('undefined os'); expect(construct).to.throw('undefined os');
}); });
@@ -213,7 +237,9 @@ describe('Application', () => {
// arrange // arrange
const os: OperatingSystem = 666; const os: OperatingSystem = 666;
// act // act
const construct = () => new ApplicationBuilder().withOs(os).construct(); const construct = () => new CategoryCollectionBuilder()
.withOs(os)
.construct();
// assert // assert
expect(construct).to.throw(`os "${os}" is out of range`); expect(construct).to.throw(`os "${os}" is out of range`);
}); });
@@ -223,7 +249,9 @@ describe('Application', () => {
// arrange // arrange
const expected = getValidScriptingDefinition(); const expected = getValidScriptingDefinition();
// act // act
const sut = new ApplicationBuilder().withScripting(expected).construct(); const sut = new CategoryCollectionBuilder()
.withScripting(expected)
.construct();
// assert // assert
expect(sut.scripting).to.deep.equal(expected); expect(sut.scripting).to.deep.equal(expected);
}); });
@@ -232,7 +260,9 @@ describe('Application', () => {
const scriptingDefinition = undefined; const scriptingDefinition = undefined;
// act // act
function construct() { function construct() {
return new ApplicationBuilder().withScripting(scriptingDefinition).construct(); return new CategoryCollectionBuilder()
.withScripting(scriptingDefinition)
.construct();
} }
// assert // assert
expect(construct).to.throw('undefined scripting definition'); expect(construct).to.throw('undefined scripting definition');
@@ -249,7 +279,7 @@ function getValidScriptingDefinition(): IScriptingDefinition {
}; };
} }
class ApplicationBuilder { class CategoryCollectionBuilder {
private os = OperatingSystem.Windows; private os = OperatingSystem.Windows;
private info = new ProjectInformation('name', 'repo', '0.1.0', 'homepage'); private info = new ProjectInformation('name', 'repo', '0.1.0', 'homepage');
private actions: readonly ICategory[] = [ private actions: readonly ICategory[] = [
@@ -258,23 +288,23 @@ class ApplicationBuilder {
new ScriptStub('S2').withLevel(RecommendationLevel.Strict)), new ScriptStub('S2').withLevel(RecommendationLevel.Strict)),
]; ];
private script: IScriptingDefinition = getValidScriptingDefinition(); private script: IScriptingDefinition = getValidScriptingDefinition();
public withOs(os: OperatingSystem): ApplicationBuilder { public withOs(os: OperatingSystem): CategoryCollectionBuilder {
this.os = os; this.os = os;
return this; return this;
} }
public withInfo(info: IProjectInformation) { public withInfo(info: IProjectInformation): CategoryCollectionBuilder {
this.info = info; this.info = info;
return this; return this;
} }
public withActions(actions: readonly ICategory[]) { public withActions(actions: readonly ICategory[]): CategoryCollectionBuilder {
this.actions = actions; this.actions = actions;
return this; return this;
} }
public withScripting(script: IScriptingDefinition) { public withScripting(script: IScriptingDefinition): CategoryCollectionBuilder {
this.script = script; this.script = script;
return this; return this;
} }
public construct(): Application { public construct(): CategoryCollection {
return new Application(this.os, this.info, this.actions, this.script); return new CategoryCollection(this.os, this.info, this.actions, this.script);
} }
} }

View File

@@ -4,7 +4,7 @@ import { getScriptNodeId, getScriptId, getCategoryNodeId, getCategoryId } from '
import { CategoryStub } from '../../../stubs/CategoryStub'; import { CategoryStub } from '../../../stubs/CategoryStub';
import { ScriptStub } from '../../../stubs/ScriptStub'; import { ScriptStub } from '../../../stubs/ScriptStub';
import { parseSingleCategory, parseAllCategories } from '@/presentation/Scripts/ScriptsTree/ScriptNodeParser'; import { parseSingleCategory, parseAllCategories } from '@/presentation/Scripts/ScriptsTree/ScriptNodeParser';
import { ApplicationStub } from '../../../stubs/ApplicationStub'; import { CategoryCollectionStub } from '../../../stubs/CategoryCollectionStub';
import { INode, NodeType } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/INode'; import { INode, NodeType } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/INode';
import { IScript } from '@/domain/IScript'; import { IScript } from '@/domain/IScript';
import { ICategory } from '@/domain/ICategory'; import { ICategory } from '@/domain/ICategory';
@@ -36,11 +36,11 @@ describe('ScriptNodeParser', () => {
const secondSubCategory = new CategoryStub(categoryId) const secondSubCategory = new CategoryStub(categoryId)
.withCategory(new CategoryStub(33).withScriptIds('331', '331')) .withCategory(new CategoryStub(33).withScriptIds('331', '331'))
.withCategory(new CategoryStub(44).withScriptIds('44')); .withCategory(new CategoryStub(44).withScriptIds('44'));
const app = new ApplicationStub().withAction(new CategoryStub(categoryId) const collection = new CategoryCollectionStub().withAction(new CategoryStub(categoryId)
.withCategory(firstSubCategory) .withCategory(firstSubCategory)
.withCategory(secondSubCategory)); .withCategory(secondSubCategory));
// act // act
const nodes = parseSingleCategory(categoryId, app); const nodes = parseSingleCategory(categoryId, collection);
// assert // assert
expect(nodes).to.have.lengthOf(2); expect(nodes).to.have.lengthOf(2);
expectSameCategory(nodes[0], firstSubCategory); expectSameCategory(nodes[0], firstSubCategory);
@@ -50,9 +50,10 @@ describe('ScriptNodeParser', () => {
// arrange // arrange
const categoryId = 31; const categoryId = 31;
const scripts = [ new ScriptStub('script1'), new ScriptStub('script2'), new ScriptStub('script3') ]; const scripts = [ new ScriptStub('script1'), new ScriptStub('script2'), new ScriptStub('script3') ];
const app = new ApplicationStub().withAction(new CategoryStub(categoryId).withScripts(...scripts)); const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(categoryId).withScripts(...scripts));
// act // act
const nodes = parseSingleCategory(categoryId, app); const nodes = parseSingleCategory(categoryId, collection);
// assert // assert
expect(nodes).to.have.lengthOf(3); expect(nodes).to.have.lengthOf(3);
expectSameScript(nodes[0], scripts[0]); expectSameScript(nodes[0], scripts[0]);
@@ -63,18 +64,18 @@ describe('ScriptNodeParser', () => {
it('parseAllCategories parses as expected', () => { it('parseAllCategories parses as expected', () => {
// arrange // arrange
const app = new ApplicationStub() const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(0).withScriptIds('1, 2')) .withAction(new CategoryStub(0).withScriptIds('1, 2'))
.withAction(new CategoryStub(1).withCategories( .withAction(new CategoryStub(1).withCategories(
new CategoryStub(3).withScriptIds('3', '4'), new CategoryStub(3).withScriptIds('3', '4'),
new CategoryStub(4).withCategory(new CategoryStub(5).withScriptIds('6')), new CategoryStub(4).withCategory(new CategoryStub(5).withScriptIds('6')),
)); ));
// act // act
const nodes = parseAllCategories(app); const nodes = parseAllCategories(collection);
// assert // assert
expect(nodes).to.have.lengthOf(2); expect(nodes).to.have.lengthOf(2);
expectSameCategory(nodes[0], app.actions[0]); expectSameCategory(nodes[0], collection.actions[0]);
expectSameCategory(nodes[1], app.actions[1]); expectSameCategory(nodes[1], collection.actions[1]);
}); });
}); });

View File

@@ -4,7 +4,7 @@ import { ScriptStub } from '../../../../../../stubs/ScriptStub';
import { CategoryReverter } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/CategoryReverter'; import { CategoryReverter } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/CategoryReverter';
import { getCategoryNodeId } from '@/presentation/Scripts/ScriptsTree/ScriptNodeParser'; import { getCategoryNodeId } from '@/presentation/Scripts/ScriptsTree/ScriptNodeParser';
import { CategoryStub } from '../../../../../../stubs/CategoryStub'; import { CategoryStub } from '../../../../../../stubs/CategoryStub';
import { ApplicationStub } from '../../../../../../stubs/ApplicationStub'; import { CategoryCollectionStub } from '../../../../../../stubs/CategoryCollectionStub';
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';
@@ -17,8 +17,8 @@ describe('CategoryReverter', () => {
]; ];
const category = new CategoryStub(1).withScripts(...scripts); const category = new CategoryStub(1).withScripts(...scripts);
const nodeId = getCategoryNodeId(category); const nodeId = getCategoryNodeId(category);
const app = new ApplicationStub().withAction(category); const collection = new CategoryCollectionStub().withAction(category);
const sut = new CategoryReverter(nodeId, app); const sut = new CategoryReverter(nodeId, collection);
const testCases = [ const testCases = [
{ {
name: 'false when subscripts are not reverted', name: 'false when subscripts are not reverted',
@@ -52,7 +52,7 @@ describe('CategoryReverter', () => {
new ScriptStub('revertable2').withRevertCode('REM revert me 2'), new ScriptStub('revertable2').withRevertCode('REM revert me 2'),
]; ];
const category = new CategoryStub(1).withScripts(...scripts); const category = new CategoryStub(1).withScripts(...scripts);
const app = new ApplicationStub().withAction(category); const collection = new CategoryCollectionStub().withAction(category);
const testCases = [ const testCases = [
{ {
name: 'selects with revert state when not selected', name: 'selects with revert state when not selected',
@@ -88,8 +88,8 @@ describe('CategoryReverter', () => {
const nodeId = getCategoryNodeId(category); const nodeId = getCategoryNodeId(category);
for (const testCase of testCases) { for (const testCase of testCases) {
it(testCase.name, () => { it(testCase.name, () => {
const selection = new UserSelection(app, testCase.selection); const selection = new UserSelection(collection, testCase.selection);
const sut = new CategoryReverter(nodeId, app); const sut = new CategoryReverter(nodeId, collection);
// act // act
sut.selectWithRevertState(testCase.revert, selection); sut.selectWithRevertState(testCase.revert, selection);
// assert // assert

View File

@@ -4,7 +4,7 @@ import { INode, NodeType } from '@/presentation/Scripts/ScriptsTree/SelectableTr
import { getReverter } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ReverterFactory'; import { getReverter } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ReverterFactory';
import { ScriptReverter } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ScriptReverter'; import { ScriptReverter } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ScriptReverter';
import { CategoryReverter } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/CategoryReverter'; import { CategoryReverter } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/CategoryReverter';
import { ApplicationStub } from '../../../../../../stubs/ApplicationStub'; import { CategoryCollectionStub } from '../../../../../../stubs/CategoryCollectionStub';
import { CategoryStub } from '../../../../../../stubs/CategoryStub'; import { CategoryStub } from '../../../../../../stubs/CategoryStub';
import { getScriptNodeId, getCategoryNodeId } from '@/presentation/Scripts/ScriptsTree/ScriptNodeParser'; import { getScriptNodeId, getCategoryNodeId } from '@/presentation/Scripts/ScriptsTree/ScriptNodeParser';
import { ScriptStub } from '../../../../../../stubs/ScriptStub'; import { ScriptStub } from '../../../../../../stubs/ScriptStub';
@@ -15,9 +15,10 @@ describe('ReverterFactory', () => {
// arrange // arrange
const category = new CategoryStub(0).withScriptIds('55'); const category = new CategoryStub(0).withScriptIds('55');
const node = getNodeStub(getCategoryNodeId(category), NodeType.Category); const node = getNodeStub(getCategoryNodeId(category), NodeType.Category);
const app = new ApplicationStub().withAction(category); const collection = new CategoryCollectionStub()
.withAction(category);
// act // act
const result = getReverter(node, app); const result = getReverter(node, collection);
// assert // assert
expect(result instanceof CategoryReverter).to.equal(true); expect(result instanceof CategoryReverter).to.equal(true);
}); });
@@ -25,9 +26,10 @@ describe('ReverterFactory', () => {
// arrange // arrange
const script = new ScriptStub('test'); const script = new ScriptStub('test');
const node = getNodeStub(getScriptNodeId(script), NodeType.Script); const node = getNodeStub(getScriptNodeId(script), NodeType.Script);
const app = new ApplicationStub().withAction(new CategoryStub(0).withScript(script)); const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(0).withScript(script));
// act // act
const result = getReverter(node, app); const result = getReverter(node, collection);
// assert // assert
expect(result instanceof ScriptReverter).to.equal(true); expect(result instanceof ScriptReverter).to.equal(true);
}); });

View File

@@ -1,13 +1,13 @@
import 'mocha'; import 'mocha';
import { expect } from 'chai'; import { expect } from 'chai';
import { ScriptReverter } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ScriptReverter'; import { ScriptReverter } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ScriptReverter';
import { SelectedScriptStub } from '../../../../../../stubs/SelectedScriptStub';
import { getScriptNodeId } from '@/presentation/Scripts/ScriptsTree/ScriptNodeParser'; import { getScriptNodeId } from '@/presentation/Scripts/ScriptsTree/ScriptNodeParser';
import { ScriptStub } from '../../../../../../stubs/ScriptStub';
import { UserSelection } from '@/application/Context/State/Selection/UserSelection'; import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript'; import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
import { ApplicationStub } from '../../../../../../stubs/ApplicationStub'; import { CategoryCollectionStub } from '../../../../../../stubs/CategoryCollectionStub';
import { CategoryStub } from '../../../../../../stubs/CategoryStub'; import { CategoryStub } from '../../../../../../stubs/CategoryStub';
import { ScriptStub } from '../../../../../../stubs/ScriptStub';
import { SelectedScriptStub } from '../../../../../../stubs/SelectedScriptStub';
describe('ScriptReverter', () => { describe('ScriptReverter', () => {
describe('getState', () => { describe('getState', () => {
@@ -45,7 +45,8 @@ describe('ScriptReverter', () => {
describe('selectWithRevertState', () => { describe('selectWithRevertState', () => {
// arrange // arrange
const script = new ScriptStub('id'); const script = new ScriptStub('id');
const app = new ApplicationStub().withAction(new CategoryStub(5).withScript(script)); const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(5).withScript(script));
const testCases = [ const testCases = [
{ {
name: 'selects with revert state when not selected', name: 'selects with revert state when not selected',
@@ -75,7 +76,7 @@ describe('ScriptReverter', () => {
const nodeId = getScriptNodeId(script); const nodeId = getScriptNodeId(script);
for (const testCase of testCases) { for (const testCase of testCases) {
it(testCase.name, () => { it(testCase.name, () => {
const selection = new UserSelection(app, testCase.selection); const selection = new UserSelection(collection, testCase.selection);
const sut = new ScriptReverter(nodeId); const sut = new ScriptReverter(nodeId);
// act // act
sut.selectWithRevertState(testCase.revert, selection); sut.selectWithRevertState(testCase.revert, selection);

View File

@@ -1,11 +1,13 @@
import { ScriptingDefinitionStub } from './ScriptingDefinitionStub'; import { ScriptingDefinitionStub } from './ScriptingDefinitionStub';
import { IApplication, ICategory, IScript } from '@/domain/IApplication';
import { ProjectInformation } from '@/domain/ProjectInformation'; import { ProjectInformation } from '@/domain/ProjectInformation';
import { OperatingSystem } from '@/domain/OperatingSystem'; import { OperatingSystem } from '@/domain/OperatingSystem';
import { ScriptStub } from './ScriptStub'; import { ScriptStub } from './ScriptStub';
import { IScriptingDefinition } from '@/domain/IScriptingDefinition'; import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
import { IScript } from '@/domain/IScript';
import { ICategory } from '@/domain/ICategory';
import { ICategoryCollection } from '@/domain/ICategoryCollection';
export class ApplicationStub implements IApplication { export class CategoryCollectionStub implements ICategoryCollection {
public scripting: IScriptingDefinition = new ScriptingDefinitionStub(); public scripting: IScriptingDefinition = new ScriptingDefinitionStub();
public os = OperatingSystem.Linux; public os = OperatingSystem.Linux;
public initialScript: IScript = new ScriptStub('55'); public initialScript: IScript = new ScriptStub('55');
@@ -14,19 +16,19 @@ export class ApplicationStub implements IApplication {
public readonly info = new ProjectInformation('StubApplication', '0.1.0', 'https://github.com/undergroundwires/privacy.sexy', 'https://privacy.sexy'); 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 readonly actions = new Array<ICategory>();
public withAction(category: ICategory): ApplicationStub { public withAction(category: ICategory): CategoryCollectionStub {
this.actions.push(category); this.actions.push(category);
return this; return this;
} }
public withOs(os: OperatingSystem): ApplicationStub { public withOs(os: OperatingSystem): CategoryCollectionStub {
this.os = os; this.os = os;
return this; return this;
} }
public withScripting(scripting: IScriptingDefinition): ApplicationStub { public withScripting(scripting: IScriptingDefinition): CategoryCollectionStub {
this.scripting = scripting; this.scripting = scripting;
return this; return this;
} }
public withInitialScript(script: IScript): ApplicationStub { public withInitialScript(script: IScript): CategoryCollectionStub {
this.initialScript = script; this.initialScript = script;
return this; return this;
} }