diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dd3a2160..3eca5bc0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ - **Stateless**, extends `Vue` - **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 - - 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. ## License diff --git a/README.md b/README.md index 873a298d..688387b5 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ - Event driven as in components simply listens to events from the state and act accordingly. - **Application Layer** - 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)) ![DDD + vue.js](img/architecture/app-ddd.png) diff --git a/src/application/Context/ApplicationContext.ts b/src/application/Context/ApplicationContext.ts index e0b762e4..da277938 100644 --- a/src/application/Context/ApplicationContext.ts +++ b/src/application/Context/ApplicationContext.ts @@ -1,20 +1,20 @@ import { IApplicationContext } from './IApplicationContext'; -import { IApplication } from '@/domain/IApplication'; -import { IApplicationState } from './State/IApplicationState'; -import { ApplicationState } from './State/ApplicationState'; +import { ICategoryCollectionState } from './State/ICategoryCollectionState'; +import { CategoryCollectionState } from './State/CategoryCollectionState'; 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 { - const application = parseApplication(applicationFile); + const application = parseCategoryCollection(applicationFile); const context = new ApplicationContext(application); return context; } export class ApplicationContext implements IApplicationContext { - public readonly state: IApplicationState; - public constructor(public readonly app: IApplication) { - this.state = new ApplicationState(app); + public readonly state: ICategoryCollectionState; + public constructor(public readonly collection: ICategoryCollection) { + this.state = new CategoryCollectionState(collection); } } diff --git a/src/application/Context/ApplicationContextProvider.ts b/src/application/Context/ApplicationContextProvider.ts index 9207cd7e..6f2b9abc 100644 --- a/src/application/Context/ApplicationContextProvider.ts +++ b/src/application/Context/ApplicationContextProvider.ts @@ -1,9 +1,9 @@ import { ApplicationContext } from './ApplicationContext'; import { IApplicationContext } from '@/application/Context/IApplicationContext'; 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 { - const application = parseApplication(applicationFile); + const application = parseCategoryCollection(applicationFile); return new ApplicationContext(application); } diff --git a/src/application/Context/IApplicationContext.ts b/src/application/Context/IApplicationContext.ts index 50afa734..9aac8594 100644 --- a/src/application/Context/IApplicationContext.ts +++ b/src/application/Context/IApplicationContext.ts @@ -1,7 +1,7 @@ -import { IApplication } from '@/domain/IApplication'; -import { IApplicationState } from './State/IApplicationState'; +import { ICategoryCollectionState } from './State/ICategoryCollectionState'; +import { ICategoryCollection } from '@/domain/ICategoryCollection'; export interface IApplicationContext { - readonly app: IApplication; - readonly state: IApplicationState; + readonly collection: ICategoryCollection; + readonly state: ICategoryCollectionState; } diff --git a/src/application/Context/State/ApplicationState.ts b/src/application/Context/State/CategoryCollectionState.ts similarity index 50% rename from src/application/Context/State/ApplicationState.ts rename to src/application/Context/State/CategoryCollectionState.ts index 30288239..ee30e8af 100644 --- a/src/application/Context/State/ApplicationState.ts +++ b/src/application/Context/State/CategoryCollectionState.ts @@ -3,18 +3,18 @@ import { IUserFilter } from './Filter/IUserFilter'; import { ApplicationCode } from './Code/ApplicationCode'; import { UserSelection } from './Selection/UserSelection'; import { IUserSelection } from './Selection/IUserSelection'; -import { IApplicationState } from './IApplicationState'; -import { IApplication } from '@/domain/IApplication'; +import { ICategoryCollectionState } from './ICategoryCollectionState'; 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 selection: IUserSelection; public readonly filter: IUserFilter; - public constructor(readonly app: IApplication) { - this.selection = new UserSelection(app, []); - this.code = new ApplicationCode(this.selection, app.scripting); - this.filter = new UserFilter(app); + public constructor(readonly collection: ICategoryCollection) { + this.selection = new UserSelection(collection, []); + this.code = new ApplicationCode(this.selection, collection.scripting); + this.filter = new UserFilter(collection); } } diff --git a/src/application/Context/State/Filter/UserFilter.ts b/src/application/Context/State/Filter/UserFilter.ts index 452ed36f..8fcc9ff0 100644 --- a/src/application/Context/State/Filter/UserFilter.ts +++ b/src/application/Context/State/Filter/UserFilter.ts @@ -1,16 +1,16 @@ import { IScript } from '@/domain/IScript'; import { FilterResult } from './FilterResult'; import { IFilterResult } from './IFilterResult'; -import { IApplication } from '@/domain/IApplication'; import { IUserFilter } from './IUserFilter'; import { Signal } from '@/infrastructure/Events/Signal'; +import { ICategoryCollection } from '@/domain/ICategoryCollection'; export class UserFilter implements IUserFilter { public readonly filtered = new Signal(); public readonly filterRemoved = new Signal(); 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'); } const filterLowercase = filter.toLocaleLowerCase(); - const filteredScripts = this.application.getAllScripts().filter( + const filteredScripts = this.collection.getAllScripts().filter( (script) => isScriptAMatch(script, filterLowercase)); - const filteredCategories = this.application.getAllCategories().filter( + const filteredCategories = this.collection.getAllCategories().filter( (category) => category.name.toLowerCase().includes(filterLowercase)); const matches = new FilterResult( filteredScripts, diff --git a/src/application/Context/State/IApplicationState.ts b/src/application/Context/State/ICategoryCollectionState.ts similarity index 88% rename from src/application/Context/State/IApplicationState.ts rename to src/application/Context/State/ICategoryCollectionState.ts index 1eb2caf0..21482f8f 100644 --- a/src/application/Context/State/IApplicationState.ts +++ b/src/application/Context/State/ICategoryCollectionState.ts @@ -3,7 +3,7 @@ import { IUserSelection } from './Selection/IUserSelection'; import { IApplicationCode } from './Code/IApplicationCode'; export { IUserSelection, IApplicationCode, IUserFilter }; -export interface IApplicationState { +export interface ICategoryCollectionState { readonly code: IApplicationCode; readonly filter: IUserFilter; readonly selection: IUserSelection; diff --git a/src/application/Context/State/Selection/UserSelection.ts b/src/application/Context/State/Selection/UserSelection.ts index b987d0c8..82e5280c 100644 --- a/src/application/Context/State/Selection/UserSelection.ts +++ b/src/application/Context/State/Selection/UserSelection.ts @@ -1,17 +1,18 @@ import { SelectedScript } from './SelectedScript'; -import { IApplication, ICategory } from '@/domain/IApplication'; import { IUserSelection } from './IUserSelection'; import { InMemoryRepository } from '@/infrastructure/Repository/InMemoryRepository'; import { IScript } from '@/domain/IScript'; import { Signal } from '@/infrastructure/Events/Signal'; import { IRepository } from '@/infrastructure/Repository/IRepository'; +import { ICategory } from '@/domain/ICategory'; +import { ICategoryCollection } from '@/domain/ICategoryCollection'; export class UserSelection implements IUserSelection { public readonly changed = new Signal>(); private readonly scripts: IRepository; constructor( - private readonly app: IApplication, + private readonly collection: ICategoryCollection, selectedScripts: ReadonlyArray) { this.scripts = new InMemoryRepository(); if (selectedScripts && selectedScripts.length > 0) { @@ -40,7 +41,7 @@ export class UserSelection implements IUserSelection { } public removeAllInCategory(categoryId: number): void { - const category = this.app.findCategory(categoryId); + const category = this.collection.findCategory(categoryId); const scriptsToRemove = category.getAllScriptsRecursively() .filter((script) => this.scripts.exists(script.id)); if (!scriptsToRemove.length) { @@ -53,7 +54,7 @@ export class UserSelection implements IUserSelection { } public addOrUpdateAllInCategory(categoryId: number, revert: boolean = false): void { - const category = this.app.findCategory(categoryId); + const category = this.collection.findCategory(categoryId); const scriptsToAddOrUpdate = category.getAllScriptsRecursively() .filter((script) => !this.scripts.exists(script.id) @@ -70,7 +71,7 @@ export class UserSelection implements IUserSelection { } public addSelectedScript(scriptId: string, revert: boolean): void { - const script = this.app.findScript(scriptId); + const script = this.collection.findScript(scriptId); if (!script) { 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 { - const script = this.app.findScript(scriptId); + const script = this.collection.findScript(scriptId); const selectedScript = new SelectedScript(script, revert); this.scripts.addOrUpdateItem(selectedScript); this.changed.notify(this.scripts.getItems()); @@ -105,7 +106,7 @@ export class UserSelection implements IUserSelection { } public selectAll(): void { - for (const script of this.app.getAllScripts()) { + for (const script of this.collection.getAllScripts()) { if (!this.scripts.exists(script.id)) { const selection = new SelectedScript(script, false); this.scripts.addItem(selection); diff --git a/src/application/Parser/ApplicationParser.ts b/src/application/Parser/CategoryCollectionParser.ts similarity index 73% rename from src/application/Parser/ApplicationParser.ts rename to src/application/Parser/CategoryCollectionParser.ts index 2950a13e..0231bb5f 100644 --- a/src/application/Parser/ApplicationParser.ts +++ b/src/application/Parser/CategoryCollectionParser.ts @@ -1,6 +1,4 @@ import { Category } from '@/domain/Category'; -import { Application } from '@/domain/Application'; -import { IApplication } from '@/domain/IApplication'; import { YamlApplication } from 'js-yaml-loader!@/application.yaml'; import { parseCategory } from './CategoryParser'; import { parseProjectInformation } from './ProjectInformationParser'; @@ -8,11 +6,13 @@ import { ScriptCompiler } from './Compiler/ScriptCompiler'; import { OperatingSystem } from '@/domain/OperatingSystem'; import { parseScriptingDefinition } from './ScriptingDefinitionParser'; import { createEnumParser } from '../Common/Enum'; +import { ICategoryCollection } from '@/domain/ICategoryCollection'; +import { CategoryCollection } from '@/domain/CategoryCollection'; -export function parseApplication( +export function parseCategoryCollection( content: YamlApplication, env: NodeJS.ProcessEnv = process.env, - osParser = createEnumParser(OperatingSystem)): IApplication { + osParser = createEnumParser(OperatingSystem)): ICategoryCollection { validate(content); const compiler = new ScriptCompiler(content.functions); const categories = new Array(); @@ -23,19 +23,19 @@ export function parseApplication( const os = osParser.parseEnum(content.os, 'os'); const info = parseProjectInformation(env); const scripting = parseScriptingDefinition(content.scripting, info); - const app = new Application( + const collection = new CategoryCollection( os, info, categories, scripting); - return app; + return collection; } function validate(content: YamlApplication): void { 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) { - throw new Error('application does not define any action'); + throw new Error('content does not define any action'); } } diff --git a/src/domain/Application.ts b/src/domain/CategoryCollection.ts similarity index 91% rename from src/domain/Application.ts rename to src/domain/CategoryCollection.ts index abdee6b1..6678e092 100644 --- a/src/domain/Application.ts +++ b/src/domain/CategoryCollection.ts @@ -2,17 +2,17 @@ import { getEnumNames, getEnumValues } from '@/application/Common/Enum'; import { IEntity } from '../infrastructure/Entity/IEntity'; import { ICategory } from './ICategory'; import { IScript } from './IScript'; -import { IApplication } from './IApplication'; import { IProjectInformation } from './IProjectInformation'; import { RecommendationLevel } from './RecommendationLevel'; import { OperatingSystem } from './OperatingSystem'; 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 totalCategories(): number { return this.queryable.allCategories.length; } - private readonly queryable: IQueryableApplication; + private readonly queryable: IQueryableCollection; constructor( public readonly os: OperatingSystem, @@ -89,26 +89,26 @@ function ensureNoDuplicates(entities: ReadonlyArray>) { } } -interface IQueryableApplication { +interface IQueryableCollection { allCategories: ICategory[]; allScripts: IScript[]; scriptsByLevel: Map; } -function ensureValid(application: IQueryableApplication) { +function ensureValid(application: IQueryableCollection) { ensureValidCategories(application.allCategories); ensureValidScripts(application.allScripts); } function ensureValidCategories(allCategories: readonly ICategory[]) { 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[]) { 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)) { if (allScripts.every((script) => script.level !== level)) { @@ -130,7 +130,7 @@ function flattenApplication(categories: ReadonlyArray): [ICategory[], function flattenCategories( categories: ReadonlyArray, allCategories: ICategory[], - allScripts: IScript[]): IQueryableApplication { + allScripts: IScript[]): IQueryableCollection { if (!categories || categories.length === 0) { return; } @@ -153,7 +153,7 @@ function flattenScripts( } function makeQueryable( - actions: ReadonlyArray): IQueryableApplication { + actions: ReadonlyArray): IQueryableCollection { const flattened = flattenApplication(actions); return { allCategories: flattened[0], diff --git a/src/domain/IApplication.ts b/src/domain/ICategoryCollection.ts similarity index 63% rename from src/domain/IApplication.ts rename to src/domain/ICategoryCollection.ts index 53027700..25bf3514 100644 --- a/src/domain/IApplication.ts +++ b/src/domain/ICategoryCollection.ts @@ -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 { ICategory } from '@/domain/ICategory'; -import { IProjectInformation } from './IProjectInformation'; -import { RecommendationLevel } from './RecommendationLevel'; -import { OperatingSystem } from './OperatingSystem'; -import { IScriptingDefinition } from './IScriptingDefinition'; +import { IProjectInformation } from '@/domain/IProjectInformation'; -export interface IApplication { +export interface ICategoryCollection { readonly info: IProjectInformation; readonly scripting: IScriptingDefinition; - readonly os: OperatingSystem; readonly totalScripts: number; readonly totalCategories: number; @@ -20,6 +19,3 @@ export interface IApplication { getAllScripts(): ReadonlyArray; getAllCategories(): ReadonlyArray; } - -export { IScript } from '@/domain/IScript'; -export { ICategory } from '@/domain/ICategory'; diff --git a/src/presentation/Scripts/Cards/CardList.vue b/src/presentation/Scripts/Cards/CardList.vue index a4992e78..d61e7f97 100644 --- a/src/presentation/Scripts/Cards/CardList.vue +++ b/src/presentation/Scripts/Cards/CardList.vue @@ -33,7 +33,7 @@ export default class CardList extends StatefulVue { public async mounted() { const context = await this.getCurrentContextAsync(); - this.setCategories(context.app.actions); + this.setCategories(context.collection.actions); this.onOutsideOfActiveCardClicked((element) => { if (hasDirective(element)) { return; diff --git a/src/presentation/Scripts/Cards/CardListItem.vue b/src/presentation/Scripts/Cards/CardListItem.vue index e429fb28..092752c6 100644 --- a/src/presentation/Scripts/Cards/CardListItem.vue +++ b/src/presentation/Scripts/Cards/CardListItem.vue @@ -79,7 +79,7 @@ export default class CardListItem extends StatefulVue { @Watch('categoryId') public async updateStateAsync(value: |number) { 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; const currentSelection = context.state.selection; this.isAnyChildSelected = category ? currentSelection.isAnySelected(category) : false; diff --git a/src/presentation/Scripts/ScriptsTree/ScriptNodeParser.ts b/src/presentation/Scripts/ScriptsTree/ScriptNodeParser.ts index 7e8742c8..885d635a 100644 --- a/src/presentation/Scripts/ScriptsTree/ScriptNodeParser.ts +++ b/src/presentation/Scripts/ScriptsTree/ScriptNodeParser.ts @@ -1,18 +1,18 @@ -import { IApplication } from './../../../domain/IApplication'; import { ICategory, IScript } from '@/domain/ICategory'; 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(); - for (const category of app.actions) { + for (const category of collection.actions) { const children = parseCategoryRecursively(category); nodes.push(convertCategoryToNode(category, children)); } return nodes; } -export function parseSingleCategory(categoryId: number, app: IApplication): INode[] | undefined { - const category = app.findCategory(categoryId); +export function parseSingleCategory(categoryId: number, collection: ICategoryCollection): INode[] | undefined { + const category = collection.findCategory(categoryId); if (!category) { throw new Error(`Category with id ${categoryId} does not exist`); } diff --git a/src/presentation/Scripts/ScriptsTree/ScriptsTree.vue b/src/presentation/Scripts/ScriptsTree/ScriptsTree.vue index b899621b..617e7e66 100644 --- a/src/presentation/Scripts/ScriptsTree/ScriptsTree.vue +++ b/src/presentation/Scripts/ScriptsTree/ScriptsTree.vue @@ -19,7 +19,7 @@ import { StatefulVue } from '@/presentation/StatefulVue'; import { IScript } from '@/domain/IScript'; 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 { parseAllCategories, parseSingleCategory, getScriptNodeId, getCategoryNodeId, getCategoryId, getScriptId } from './ScriptNodeParser'; import SelectableTree from './SelectableTree/SelectableTree.vue'; @@ -65,9 +65,9 @@ public async initializeNodesAsync(categoryId?: number) { const context = await this.getCurrentContextAsync(); if (categoryId) { - this.nodes = parseSingleCategory(categoryId, context.app); + this.nodes = parseSingleCategory(categoryId, context.collection); } else { - this.nodes = parseAllCategories(context.app); + this.nodes = parseAllCategories(context.collection); } this.selectedNodeIds = context.state.selection.selectedScripts .map((selected) => getScriptNodeId(selected.script)); @@ -80,13 +80,13 @@ (category: ICategory) => node.id === getCategoryNodeId(category)); } - private beginReactingToStateChanges(state: IApplicationState) { + private beginReactingToStateChanges(state: ICategoryCollectionState) { state.selection.changed.on(this.handleSelectionChanged); state.filter.filterRemoved.on(this.handleFilterRemoved); state.filter.filtered.on(this.handleFiltered); } - private setInitialState(state: IApplicationState) { + private setInitialState(state: ICategoryCollectionState) { this.initializeNodesAsync(this.categoryId); 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); if (event.isSelected) { state.selection.addOrUpdateAllInCategory(categoryId, false); @@ -122,7 +122,7 @@ 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 actualToggleState = state.selection.isSelected(scriptId); const targetToggleState = event.isSelected; diff --git a/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/RevertToggle.vue b/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/RevertToggle.vue index 9e264481..0fe7a9ca 100644 --- a/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/RevertToggle.vue +++ b/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/RevertToggle.vue @@ -37,7 +37,7 @@ @Watch('node') public async onNodeChangedAsync(node: INode) { const context = await this.getCurrentContextAsync(); - this.handler = getReverter(node, context.app); + this.handler = getReverter(node, context.collection); } public async onRevertToggledAsync() { diff --git a/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/CategoryReverter.ts b/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/CategoryReverter.ts index bda1b11e..996bdb16 100644 --- a/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/CategoryReverter.ts +++ b/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/CategoryReverter.ts @@ -1,16 +1,16 @@ import { IReverter } from './IReverter'; import { getCategoryId } from '../../../ScriptNodeParser'; import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript'; -import { IApplication } from '@/domain/IApplication'; import { ScriptReverter } from './ScriptReverter'; import { IUserSelection } from '@/application/Context/State/Selection/IUserSelection'; +import { ICategoryCollection } from '@/domain/ICategoryCollection'; export class CategoryReverter implements IReverter { private readonly categoryId: number; private readonly scriptReverters: ReadonlyArray; - constructor(nodeId: string, app: IApplication) { + constructor(nodeId: string, collection: ICategoryCollection) { this.categoryId = getCategoryId(nodeId); - this.scriptReverters = getAllSubScriptReverters(this.categoryId, app); + this.scriptReverters = getAllSubScriptReverters(this.categoryId, collection); } public getState(selectedScripts: ReadonlyArray): boolean { return this.scriptReverters.every((script) => script.getState(selectedScripts)); @@ -20,8 +20,8 @@ export class CategoryReverter implements IReverter { } } -function getAllSubScriptReverters(categoryId: number, app: IApplication) { - const category = app.findCategory(categoryId); +function getAllSubScriptReverters(categoryId: number, collection: ICategoryCollection) { + const category = collection.findCategory(categoryId); if (!category) { throw new Error(`Category with id "${categoryId}" does not exist`); } diff --git a/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/IReverter.ts b/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/IReverter.ts index 0a16a966..99815921 100644 --- a/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/IReverter.ts +++ b/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/IReverter.ts @@ -1,5 +1,5 @@ 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 { getState(selectedScripts: ReadonlyArray): boolean; diff --git a/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ReverterFactory.ts b/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ReverterFactory.ts index dfed5e5a..9c8ada7f 100644 --- a/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ReverterFactory.ts +++ b/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ReverterFactory.ts @@ -1,13 +1,13 @@ import { INode, NodeType } from '../INode'; import { IReverter } from './IReverter'; import { ScriptReverter } from './ScriptReverter'; -import { IApplication } from '@/domain/IApplication'; 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) { case NodeType.Category: - return new CategoryReverter(node.id, app); + return new CategoryReverter(node.id, collection); case NodeType.Script: return new ScriptReverter(node.id); default: diff --git a/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ScriptReverter.ts b/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ScriptReverter.ts index 415a2eb5..4ff08ac0 100644 --- a/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ScriptReverter.ts +++ b/src/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ScriptReverter.ts @@ -1,7 +1,7 @@ import { IReverter } from './IReverter'; import { getScriptId } from '../../../ScriptNodeParser'; 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 { private readonly scriptId: string; diff --git a/src/presentation/Scripts/Selector/TheSelector.vue b/src/presentation/Scripts/Selector/TheSelector.vue index c890c1b5..425cd983 100644 --- a/src/presentation/Scripts/Selector/TheSelector.vue +++ b/src/presentation/Scripts/Selector/TheSelector.vue @@ -105,13 +105,13 @@ const selectors = new Map([ [SelectionState.Standard, { select: (context) => context.state.selection.selectOnly( - context.app.getScriptsByLevel(RecommendationLevel.Standard)), + context.collection.getScriptsByLevel(RecommendationLevel.Standard)), isSelected: (context) => hasAllSelectedLevelOf(RecommendationLevel.Standard, context), }], [SelectionState.Strict, { select: (context) => - context.state.selection.selectOnly(context.app.getScriptsByLevel(RecommendationLevel.Strict)), + context.state.selection.selectOnly(context.collection.getScriptsByLevel(RecommendationLevel.Strict)), isSelected: (context) => hasAllSelectedLevelOf(RecommendationLevel.Strict, context), }], @@ -119,7 +119,7 @@ const selectors = new Map([ select: (context) => context.state.selection.selectAll(), 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) { - const scripts = context.app.getScriptsByLevel(level); + const scripts = context.collection.getScriptsByLevel(level); const selectedScripts = context.state.selection.selectedScripts; return areAllSelected(scripts, selectedScripts); } diff --git a/src/presentation/Scripts/TheScripts.vue b/src/presentation/Scripts/TheScripts.vue index 8795fc37..c50994a9 100644 --- a/src/presentation/Scripts/TheScripts.vue +++ b/src/presentation/Scripts/TheScripts.vue @@ -74,7 +74,7 @@ public async mounted() { const context = await this.getCurrentContextAsync(); - this.repositoryUrl = context.app.info.repositoryWebUrl; + this.repositoryUrl = context.collection.info.repositoryWebUrl; const filter = context.state.filter; filter.filterRemoved.on(() => { this.isSearching = false; diff --git a/src/presentation/TheCodeArea.vue b/src/presentation/TheCodeArea.vue index 055d6e65..26bd11f9 100644 --- a/src/presentation/TheCodeArea.vue +++ b/src/presentation/TheCodeArea.vue @@ -40,7 +40,7 @@ export default class TheCodeArea extends StatefulVue { public async mounted() { 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; this.editor.setValue(appCode.current || NothingChosenCode, 1); appCode.changed.on((code) => this.updateCode(code)); diff --git a/src/presentation/TheCodeButtons.vue b/src/presentation/TheCodeButtons.vue index d0999c19..6d7a08d6 100644 --- a/src/presentation/TheCodeButtons.vue +++ b/src/presentation/TheCodeButtons.vue @@ -21,7 +21,7 @@ import { SaveFileDialog, FileType } from '@/infrastructure/SaveFileDialog'; import { Clipboard } from '@/infrastructure/Clipboard'; import IconButton from './IconButton.vue'; 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 { IApplicationContext } from '@/application/Context/IApplicationContext'; @@ -61,9 +61,9 @@ export default class TheCodeButtons extends StatefulVue { } 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 type = getType(context.app.scripting.language); + const type = getType(context.collection.scripting.language); SaveFileDialog.saveFile(content, fileName, type); } diff --git a/src/presentation/TheFooter/DownloadUrlListItem.vue b/src/presentation/TheFooter/DownloadUrlListItem.vue index dc876d2c..7eb192bd 100644 --- a/src/presentation/TheFooter/DownloadUrlListItem.vue +++ b/src/presentation/TheFooter/DownloadUrlListItem.vue @@ -39,7 +39,7 @@ export default class DownloadUrlListItem extends StatefulVue { private async getDownloadUrlAsync(os: OperatingSystem): Promise { const context = await this.getCurrentContextAsync(); - return context.app.info.getDownloadUrl(os); + return context.collection.info.getDownloadUrl(os); } } diff --git a/src/presentation/TheFooter/PrivacyPolicy.vue b/src/presentation/TheFooter/PrivacyPolicy.vue index 230495e5..3e160d18 100644 --- a/src/presentation/TheFooter/PrivacyPolicy.vue +++ b/src/presentation/TheFooter/PrivacyPolicy.vue @@ -48,8 +48,8 @@ export default class TheFooter extends StatefulVue { public async mounted() { const context = await this.getCurrentContextAsync(); - this.repositoryUrl = context.app.info.repositoryWebUrl; - this.feedbackUrl = context.app.info.feedbackUrl; + this.repositoryUrl = context.collection.info.repositoryWebUrl; + this.feedbackUrl = context.collection.info.feedbackUrl; } } diff --git a/src/presentation/TheFooter/TheFooter.vue b/src/presentation/TheFooter/TheFooter.vue index f0a3d61a..74580ab4 100644 --- a/src/presentation/TheFooter/TheFooter.vue +++ b/src/presentation/TheFooter/TheFooter.vue @@ -74,8 +74,8 @@ export default class TheFooter extends StatefulVue { } public async mounted() { - const state = await this.getCurrentContextAsync(); - const info = state.app.info; + const context = await this.getCurrentContextAsync(); + const info = context.collection.info; this.version = info.version; this.homepageUrl = info.homepage; this.repositoryUrl = info.repositoryWebUrl; diff --git a/src/presentation/TheHeader.vue b/src/presentation/TheHeader.vue index 5c3be536..659d269b 100644 --- a/src/presentation/TheHeader.vue +++ b/src/presentation/TheHeader.vue @@ -16,7 +16,7 @@ export default class TheHeader extends StatefulVue { public async mounted() { const context = await this.getCurrentContextAsync(); - this.title = context.app.info.name; + this.title = context.collection.info.name; } } diff --git a/src/presentation/TheSearchBar.vue b/src/presentation/TheSearchBar.vue index 189ab505..4afd198a 100644 --- a/src/presentation/TheSearchBar.vue +++ b/src/presentation/TheSearchBar.vue @@ -13,7 +13,7 @@ import { Component, Watch } from 'vue-property-decorator'; import { StatefulVue } from './StatefulVue'; import { NonCollapsing } from '@/presentation/Scripts/Cards/NonCollapsingDirective'; -import { IUserFilter } from '@/application/Context/State/IApplicationState'; +import { IUserFilter } from '@/application/Context/State/ICategoryCollectionState'; @Component( { directives: { NonCollapsing }, @@ -25,7 +25,7 @@ export default class TheSearchBar extends StatefulVue { public async mounted() { const context = await this.getCurrentContextAsync(); - const totalScripts = context.app.totalScripts; + const totalScripts = context.collection.totalScripts; this.searchPlaceHolder = `Search in ${totalScripts} scripts`; this.beginReacting(context.state.filter); } diff --git a/tests/unit/application/Context/ApplicationContext.spec.ts b/tests/unit/application/Context/State/ApplicationContext.spec.ts similarity index 100% rename from tests/unit/application/Context/ApplicationContext.spec.ts rename to tests/unit/application/Context/State/ApplicationContext.spec.ts diff --git a/tests/unit/application/Context/State/ApplicationState.spec.ts b/tests/unit/application/Context/State/ApplicationState.spec.ts index 726714d3..fc6cec40 100644 --- a/tests/unit/application/Context/State/ApplicationState.spec.ts +++ b/tests/unit/application/Context/State/ApplicationState.spec.ts @@ -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 { 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 { ScriptStub } from '../../../stubs/ScriptStub'; +import { CategoryStub } from '../../../stubs/CategoryStub'; +import { CategoryCollectionStub } from '../../../stubs/CategoryCollectionStub'; -describe('ApplicationState', () => { +describe('CategoryCollectionState', () => { describe('code', () => { it('initialized with empty code', () => { // arrange - const app = new ApplicationStub(); - const sut = new ApplicationState(app); + const collection = new CategoryCollectionStub(); + const sut = new CategoryCollectionState(collection); // act const code = sut.code.current; // assert @@ -21,13 +21,13 @@ describe('ApplicationState', () => { }); it('reacts to selection changes as expected', () => { // arrange - const app = new ApplicationStub().withAction(new CategoryStub(0).withScriptIds('scriptId')); - const selectionStub = new UserSelection(app, []); - const expectedCodeGenerator = new ApplicationCode(selectionStub, app.scripting); + const collection = new CategoryCollectionStub().withAction(new CategoryStub(0).withScriptIds('scriptId')); + const selectionStub = new UserSelection(collection, []); + const expectedCodeGenerator = new ApplicationCode(selectionStub, collection.scripting); selectionStub.selectAll(); const expectedCode = expectedCodeGenerator.current; // act - const sut = new ApplicationState(app); + const sut = new CategoryCollectionState(collection); sut.selection.selectAll(); const actualCode = sut.code.current; // assert @@ -37,18 +37,19 @@ describe('ApplicationState', () => { describe('selection', () => { it('initialized with no selection', () => { // arrange - const app = new ApplicationStub(); - const sut = new ApplicationState(app); + const collection = new CategoryCollectionStub(); + const sut = new CategoryCollectionState(collection); // act const actual = sut.selection.totalSelected; // assert expect(actual).to.equal(0); }); - it('can select a script from current application', () => { + it('can select a script from current collection', () => { // arrange const expectedScript = new ScriptStub('scriptId'); - const app = new ApplicationStub().withAction(new CategoryStub(0).withScript(expectedScript)); - const sut = new ApplicationState(app); + const collection = new CategoryCollectionStub() + .withAction(new CategoryStub(0).withScript(expectedScript)); + const sut = new CategoryCollectionState(collection); // act sut.selection.selectAll(); // assert @@ -59,20 +60,20 @@ describe('ApplicationState', () => { describe('filter', () => { it('initialized with an empty filter', () => { // arrange - const app = new ApplicationStub(); - const sut = new ApplicationState(app); + const collection = new CategoryCollectionStub(); + const sut = new CategoryCollectionState(collection); // act const actual = sut.filter.currentFilter; // assert expect(actual).to.equal(undefined); }); - it('can match a script from current application', () => { + it('can match a script from current collection', () => { // arrange const scriptNameFilter = 'scriptName'; const expectedScript = new ScriptStub('scriptId').withName(scriptNameFilter); - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(0).withScript(expectedScript)); - const sut = new ApplicationState(app); + const sut = new CategoryCollectionState(collection); // act let actualScript: IScript; sut.filter.filtered.on((result) => actualScript = result.scriptMatches[0]); diff --git a/tests/unit/application/Context/State/Code/ApplicationCode.spec.ts b/tests/unit/application/Context/State/Code/ApplicationCode.spec.ts index 8aadffe1..f614fec1 100644 --- a/tests/unit/application/Context/State/Code/ApplicationCode.spec.ts +++ b/tests/unit/application/Context/State/Code/ApplicationCode.spec.ts @@ -1,8 +1,5 @@ import 'mocha'; 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 { ApplicationCode } from '@/application/Context/State/Code/ApplicationCode'; 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 { CodePosition } from '@/application/Context/State/Code/Position/CodePosition'; import { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition'; -import { ScriptingDefinitionStub } from '../../../../stubs/ScriptingDefinitionStub'; import { IScriptingDefinition } from '@/domain/IScriptingDefinition'; 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 @@ -20,7 +20,7 @@ describe('ApplicationCode', () => { describe('ctor', () => { it('empty when selection is empty', () => { // arrange - const selection = new UserSelection(new ApplicationStub(), []); + const selection = new UserSelection(new CategoryCollectionStub(), []); const definition = new ScriptingDefinitionStub(); const sut = new ApplicationCode(selection, definition); // act @@ -31,8 +31,8 @@ describe('ApplicationCode', () => { it('generates code from script generator when selection is not empty', () => { // arrange const scripts = [new ScriptStub('first'), new ScriptStub('second')]; - const app = new ApplicationStub().withAction(new CategoryStub(1).withScripts(...scripts)); - const selection = new UserSelection(app, scripts.map((script) => script.toSelectedScript())); + const collection = new CategoryCollectionStub().withAction(new CategoryStub(1).withScripts(...scripts)); + const selection = new UserSelection(collection, scripts.map((script) => script.toSelectedScript())); const definition = new ScriptingDefinitionStub(); const expected: IUserScript = { code: 'expected-code', @@ -53,8 +53,9 @@ describe('ApplicationCode', () => { // arrange let signaled: ICodeChangedEvent; const scripts = [new ScriptStub('first'), new ScriptStub('second')]; - const app = new ApplicationStub().withAction(new CategoryStub(1).withScripts(...scripts)); - const selection = new UserSelection(app, scripts.map((script) => new SelectedScript(script, false))); + const collection = new CategoryCollectionStub().withAction(new CategoryStub(1).withScripts(...scripts)); + const scriptsToSelect = scripts.map((script) => new SelectedScript(script, false)); + const selection = new UserSelection(collection, scriptsToSelect); const definition = new ScriptingDefinitionStub(); const sut = new ApplicationCode(selection, definition); sut.changed.on((code) => signaled = code); @@ -68,8 +69,9 @@ describe('ApplicationCode', () => { // arrange let signaled: ICodeChangedEvent; const scripts = [new ScriptStub('first'), new ScriptStub('second')]; - const app = new ApplicationStub().withAction(new CategoryStub(1).withScripts(...scripts)); - const selection = new UserSelection(app, scripts.map((script) => new SelectedScript(script, false))); + const collection = new CategoryCollectionStub().withAction(new CategoryStub(1).withScripts(...scripts)); + const scriptsToSelect = scripts.map((script) => new SelectedScript(script, false)); + const selection = new UserSelection(collection, scriptsToSelect); const definition = new ScriptingDefinitionStub(); const sut = new ApplicationCode(selection, definition); sut.changed.on((code) => signaled = code); @@ -84,8 +86,8 @@ describe('ApplicationCode', () => { it('sends scripting definition to generator', () => { // arrange const expectedDefinition = new ScriptingDefinitionStub(); - const app = new ApplicationStub(); - const selection = new UserSelection(app, []); + const collection = new CategoryCollectionStub(); + const selection = new UserSelection(collection, []); const generatorMock: IUserScriptGenerator = { buildCode: (selectedScripts, definition) => { if (definition !== expectedDefinition) { @@ -108,9 +110,9 @@ describe('ApplicationCode', () => { // arrange const expectedDefinition = new ScriptingDefinitionStub(); const scripts = [new ScriptStub('first'), new ScriptStub('second')]; - const app = new ApplicationStub().withAction(new CategoryStub(1).withScripts(...scripts)); - const selection = new UserSelection(app, scripts.map((script) => new SelectedScript(script, false))); - const scriptsToSelect = scripts.map((s) => new SelectedScript(s, false)); + const collection = new CategoryCollectionStub().withAction(new CategoryStub(1).withScripts(...scripts)); + const scriptsToSelect = scripts.map((script) => new SelectedScript(script, false)); + const selection = new UserSelection(collection, scriptsToSelect); const generatorMock: IUserScriptGenerator = { buildCode: (selectedScripts) => { if (JSON.stringify(selectedScripts) !== JSON.stringify(scriptsToSelect)) { @@ -133,10 +135,11 @@ describe('ApplicationCode', () => { // arrange let signaled: ICodeChangedEvent; const scripts = [new ScriptStub('first'), new ScriptStub('second')]; - const app = new ApplicationStub().withAction(new CategoryStub(1).withScripts(...scripts)); - const selection = new UserSelection(app, scripts.map((script) => new SelectedScript(script, false))); + const collection = new CategoryCollectionStub() + .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 scriptsToSelect = scripts.map((s) => new SelectedScript(s, false)); const totalLines = 20; const expected = new Map( [ diff --git a/tests/unit/application/Context/State/Filter/UserFilter.spec.ts b/tests/unit/application/Context/State/Filter/UserFilter.spec.ts index 1291c6f2..fca5a35b 100644 --- a/tests/unit/application/Context/State/Filter/UserFilter.spec.ts +++ b/tests/unit/application/Context/State/Filter/UserFilter.spec.ts @@ -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 { 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('removeFilter', () => { it('signals when removing filter', () => { // arrange let isCalled = false; - const sut = new UserFilter(new ApplicationStub()); + const sut = new UserFilter(new CategoryCollectionStub()); sut.filterRemoved.on(() => isCalled = true); // act sut.removeFilter(); @@ -20,7 +21,7 @@ describe('UserFilter', () => { }); it('sets currentFilter to undefined', () => { // arrange - const sut = new UserFilter(new ApplicationStub()); + const sut = new UserFilter(new CategoryCollectionStub()); // act sut.setFilter('non-important'); sut.removeFilter(); @@ -33,7 +34,7 @@ describe('UserFilter', () => { // arrange let actual: IFilterResult; const nonMatchingFilter = 'non matching filter'; - const sut = new UserFilter(new ApplicationStub()); + const sut = new UserFilter(new CategoryCollectionStub()); sut.filtered.on((filterResult) => actual = filterResult); // act sut.setFilter(nonMatchingFilter); @@ -44,7 +45,7 @@ describe('UserFilter', () => { it('sets currentFilter as expected when no matches', () => { // arrange const nonMatchingFilter = 'non matching filter'; - const sut = new UserFilter(new ApplicationStub()); + const sut = new UserFilter(new CategoryCollectionStub()); // act sut.setFilter(nonMatchingFilter); // assert @@ -61,7 +62,7 @@ describe('UserFilter', () => { let actual: IFilterResult; const script = new ScriptStub('id').withCode(code); const category = new CategoryStub(33).withScript(script); - const sut = new UserFilter(new ApplicationStub() + const sut = new UserFilter(new CategoryCollectionStub() .withAction(category)); sut.filtered.on((filterResult) => actual = filterResult); // act @@ -81,7 +82,7 @@ describe('UserFilter', () => { let actual: IFilterResult; const script = new ScriptStub('id').withRevertCode(revertCode); const category = new CategoryStub(33).withScript(script); - const sut = new UserFilter(new ApplicationStub() + const sut = new UserFilter(new CategoryCollectionStub() .withAction(category)); sut.filtered.on((filterResult) => actual = filterResult); // act @@ -101,7 +102,7 @@ describe('UserFilter', () => { let actual: IFilterResult; const script = new ScriptStub('id').withName(name); const category = new CategoryStub(33).withScript(script); - const sut = new UserFilter(new ApplicationStub() + const sut = new UserFilter(new CategoryCollectionStub() .withAction(category)); sut.filtered.on((filterResult) => actual = filterResult); // act @@ -121,7 +122,7 @@ describe('UserFilter', () => { const filter = 'Hello WoRLD'; let actual: IFilterResult; const category = new CategoryStub(55).withName(categoryName); - const sut = new UserFilter(new ApplicationStub() + const sut = new UserFilter(new CategoryCollectionStub() .withAction(category)); sut.filtered.on((filterResult) => actual = filterResult); // act @@ -144,9 +145,9 @@ describe('UserFilter', () => { const category = new CategoryStub(55) .withName(matchingText) .withScript(script); - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(category); - const sut = new UserFilter(app); + const sut = new UserFilter(collection); sut.filtered.on((filterResult) => actual = filterResult); // act sut.setFilter(filter); diff --git a/tests/unit/application/Context/State/Selection/UserSelection.spec.ts b/tests/unit/application/Context/State/Selection/UserSelection.spec.ts index de605f85..ed008efe 100644 --- a/tests/unit/application/Context/State/Selection/UserSelection.spec.ts +++ b/tests/unit/application/Context/State/Selection/UserSelection.spec.ts @@ -1,21 +1,21 @@ import 'mocha'; import { expect } from 'chai'; 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 { 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('ctor', () => { it('has nothing with no initial selection', () => { // arrange - const app = new ApplicationStub().withAction(new CategoryStub(1).withScriptIds('s1')); + const collection = new CategoryCollectionStub().withAction(new CategoryStub(1).withScriptIds('s1')); const selection = []; // act - const sut = new UserSelection(app, selection); + const sut = new UserSelection(collection, selection); // assert expect(sut.selectedScripts).to.have.lengthOf(0); }); @@ -23,11 +23,11 @@ describe('UserSelection', () => { // arrange const firstScript = new ScriptStub('1'); const secondScript = new ScriptStub('2'); - const app = new ApplicationStub().withAction( - new CategoryStub(1).withScript(firstScript).withScripts(secondScript)); + const collection = new CategoryCollectionStub() + .withAction(new CategoryStub(1).withScript(firstScript).withScripts(secondScript)); const expected = [ new SelectedScript(firstScript, false), new SelectedScript(secondScript, true) ]; // act - const sut = new UserSelection(app, expected); + const sut = new UserSelection(collection, expected); // assert expect(sut.selectedScripts).to.deep.include(expected[0]); expect(sut.selectedScripts).to.deep.include(expected[1]); @@ -36,13 +36,13 @@ describe('UserSelection', () => { it('deselectAll removes all items', () => { // arrange const events: Array = []; - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(1) - .withScriptIds('s1', 's2', 's3', 's4')); + .withScriptIds('s1', 's2', 's3', 's4')); const selectedScripts = [ 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)); // act sut.deselectAll(); @@ -54,13 +54,13 @@ describe('UserSelection', () => { it('selectOnly selects expected', () => { // arrange const events: Array = []; - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(1) - .withScriptIds('s1', 's2', 's3', 's4')); + .withScriptIds('s1', 's2', 's3', 's4')); const selectedScripts = [ 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)); const scripts = [new ScriptStub('s2'), new ScriptStub('s3'), new ScriptStub('s4')]; const expected = [ new SelectedScriptStub('s2'), new SelectedScriptStub('s3'), @@ -78,10 +78,10 @@ describe('UserSelection', () => { // arrange const events: Array = []; 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) - .withScripts(...scripts)); - const sut = new UserSelection(app, []); + .withScripts(...scripts)); + const sut = new UserSelection(collection, []); sut.changed.on((newScripts) => events.push(newScripts)); const expected = scripts.map((script) => new SelectedScript(script, false)); // act @@ -95,10 +95,10 @@ describe('UserSelection', () => { it('adds when item does not exist', () => { // arrange const events: Array = []; - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(1) - .withScripts(new ScriptStub('s1'), new ScriptStub('s2'))); - const sut = new UserSelection(app, []); + .withScripts(new ScriptStub('s1'), new ScriptStub('s2'))); + const sut = new UserSelection(collection, []); sut.changed.on((scripts) => events.push(scripts)); const expected = [ new SelectedScript(new ScriptStub('s1'), false) ]; // act @@ -111,10 +111,10 @@ describe('UserSelection', () => { it('updates when item exists', () => { // arrange const events: Array = []; - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(1) - .withScripts(new ScriptStub('s1'), new ScriptStub('s2'))); - const sut = new UserSelection(app, []); + .withScripts(new ScriptStub('s1'), new ScriptStub('s2'))); + const sut = new UserSelection(collection, []); sut.changed.on((scripts) => events.push(scripts)); const expected = [ new SelectedScript(new ScriptStub('s1'), true) ]; // act @@ -130,10 +130,10 @@ describe('UserSelection', () => { // arrange const events: Array = []; const categoryId = 1; - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(categoryId) - .withScripts(new ScriptStub('s1'), new ScriptStub('s2'))); - const sut = new UserSelection(app, []); + .withScripts(new ScriptStub('s1'), new ScriptStub('s2'))); + const sut = new UserSelection(collection, []); sut.changed.on((s) => events.push(s)); // act sut.removeAllInCategory(categoryId); @@ -145,10 +145,10 @@ describe('UserSelection', () => { // arrange const categoryId = 1; const scripts = [new SelectedScriptStub('s1'), new SelectedScriptStub('s2')]; - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(categoryId) - .withScripts(...scripts.map((script) => script.script))); - const sut = new UserSelection(app, scripts); + .withScripts(...scripts.map((script) => script.script))); + const sut = new UserSelection(collection, scripts); // act sut.removeAllInCategory(categoryId); // assert @@ -160,10 +160,10 @@ describe('UserSelection', () => { const categoryId = 1; const existing = [new ScriptStub('s1'), new ScriptStub('s2')]; const notExisting = [new ScriptStub('s3'), new ScriptStub('s4')]; - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(categoryId) - .withScripts(...existing, ...notExisting)); - const sut = new UserSelection(app, existing.map((script) => new SelectedScript(script, false))); + .withScripts(...existing, ...notExisting)); + const sut = new UserSelection(collection, existing.map((script) => new SelectedScript(script, false))); // act sut.removeAllInCategory(categoryId); // assert @@ -177,10 +177,10 @@ describe('UserSelection', () => { const events: Array = []; const categoryId = 1; const scripts = [new ScriptStub('s1'), new ScriptStub('s2')]; - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(categoryId) - .withScripts(...scripts)); - const sut = new UserSelection(app, scripts.map((script) => new SelectedScript(script, false))); + .withScripts(...scripts)); + const sut = new UserSelection(collection, scripts.map((script) => new SelectedScript(script, false))); sut.changed.on((s) => events.push(s)); // act sut.addOrUpdateAllInCategory(categoryId); @@ -193,10 +193,10 @@ describe('UserSelection', () => { // arrange const categoryId = 1; const expected = [new ScriptStub('s1'), new ScriptStub('s2')]; - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(categoryId) - .withScripts(...expected)); - const sut = new UserSelection(app, []); + .withScripts(...expected)); + const sut = new UserSelection(collection, []); // act sut.addOrUpdateAllInCategory(categoryId); // assert @@ -207,10 +207,10 @@ describe('UserSelection', () => { // arrange const categoryId = 1; const expected = [new ScriptStub('s1'), new ScriptStub('s2')]; - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(categoryId) - .withScripts(...expected)); - const sut = new UserSelection(app, []); + .withScripts(...expected)); + const sut = new UserSelection(collection, []); // act sut.addOrUpdateAllInCategory(categoryId, true); // assert @@ -223,10 +223,10 @@ describe('UserSelection', () => { const notExisting = [ new ScriptStub('notExisting1'), new ScriptStub('notExisting2') ]; const existing = [ new ScriptStub('existing1'), new ScriptStub('existing2') ]; const allScripts = [ ...existing, ...notExisting ]; - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(categoryId) .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 sut.addOrUpdateAllInCategory(categoryId, true); // assert @@ -239,10 +239,10 @@ describe('UserSelection', () => { const notExisting = [ new ScriptStub('notExisting1'), new ScriptStub('notExisting2') ]; const existing = [ new ScriptStub('existing1'), new ScriptStub('existing2') ]; const allScripts = [ ...existing, ...notExisting ]; - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(categoryId) .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 sut.addOrUpdateAllInCategory(categoryId, true); // assert @@ -253,10 +253,10 @@ describe('UserSelection', () => { // arrange const categoryId = 1; const scripts = [ new ScriptStub('existing1'), new ScriptStub('existing2') ]; - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(categoryId) .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 sut.addOrUpdateAllInCategory(categoryId, true); // assert @@ -269,10 +269,10 @@ describe('UserSelection', () => { // arrange const selectedScript = new ScriptStub('selected'); const notSelectedScript = new ScriptStub('not selected'); - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(1) .withScripts(selectedScript, notSelectedScript)); - const sut = new UserSelection(app, [ new SelectedScript(selectedScript, false) ]); + const sut = new UserSelection(collection, [ new SelectedScript(selectedScript, false) ]); // act const actual = sut.isSelected(notSelectedScript.id); // assert @@ -282,10 +282,10 @@ describe('UserSelection', () => { // arrange const selectedScript = new ScriptStub('selected'); const notSelectedScript = new ScriptStub('not selected'); - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(1) .withScripts(selectedScript, notSelectedScript)); - const sut = new UserSelection(app, [ new SelectedScript(selectedScript, false) ]); + const sut = new UserSelection(collection, [ new SelectedScript(selectedScript, false) ]); // act const actual = sut.isSelected(selectedScript.id); // assert @@ -297,8 +297,8 @@ describe('UserSelection', () => { // arrange const category = new CategoryStub(1) .withScriptIds('non-selected-script-1', 'non-selected-script-2'); - const app = new ApplicationStub().withAction(category); - const sut = new UserSelection(app, [ ]); + const collection = new CategoryCollectionStub().withAction(category); + const sut = new UserSelection(collection, [ ]); it('areAllSelected returns false', () => { // act const actual = sut.areAllSelected(category); @@ -317,10 +317,10 @@ describe('UserSelection', () => { const category = new CategoryStub(1) .withScriptIds('non-selected-script-1', 'non-selected-script-2'); const selectedScript = new ScriptStub('selected'); - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(category) .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', () => { // act const actual = sut.areAllSelected(category); @@ -340,8 +340,8 @@ describe('UserSelection', () => { const category = new CategoryStub(1) .withScriptIds('non-selected-script-1', 'non-selected-script-2') .withCategory(new CategoryStub(12).withScript(selectedScript)); - const app = new ApplicationStub().withAction(category); - const sut = new UserSelection(app, [ new SelectedScript(selectedScript, false) ]); + const collection = new CategoryCollectionStub().withAction(category); + const sut = new UserSelection(collection, [ new SelectedScript(selectedScript, false) ]); it('areAllSelected returns false', () => { // act const actual = sut.areAllSelected(category); @@ -362,8 +362,8 @@ describe('UserSelection', () => { const category = new CategoryStub(1) .withScript(firstSelectedScript) .withCategory(new CategoryStub(12).withScript(secondSelectedScript)); - const app = new ApplicationStub().withAction(category); - const sut = new UserSelection(app, + const collection = new CategoryCollectionStub().withAction(category); + const sut = new UserSelection(collection, [ firstSelectedScript, secondSelectedScript ].map((s) => new SelectedScript(s, false))); it('areAllSelected returns true', () => { // act @@ -378,6 +378,5 @@ describe('UserSelection', () => { expect(actual).to.equal(true); }); }); - }); }); diff --git a/tests/unit/application/Parser/ApplicationParser.spec.ts b/tests/unit/application/Parser/CategoryCollectionParser.spec.ts similarity index 75% rename from tests/unit/application/Parser/ApplicationParser.spec.ts rename to tests/unit/application/Parser/CategoryCollectionParser.spec.ts index fd5588e1..cbca5fba 100644 --- a/tests/unit/application/Parser/ApplicationParser.spec.ts +++ b/tests/unit/application/Parser/CategoryCollectionParser.spec.ts @@ -1,6 +1,6 @@ import { IEntity } from '@/infrastructure/Entity/IEntity'; 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 { expect } from 'chai'; import { parseCategory } from '@/application/Parser/CategoryParser'; @@ -12,47 +12,49 @@ import { ScriptingLanguage } from '@/domain/ScriptingLanguage'; import { parseScriptingDefinition } from '@/application/Parser/ScriptingDefinitionParser'; import { mockEnumParser } from '../../stubs/EnumParserStub'; -describe('ApplicationParser', () => { - describe('parseApplication', () => { +describe('CategoryCollectionParser', () => { + describe('parseCategoryCollection', () => { it('can parse current application file', () => { // act - const act = () => parseApplication(applicationFile); + const act = () => parseCategoryCollection(applicationFile); // assert expect(act).to.not.throw(); }); it('throws when undefined', () => { // arrange - const expectedError = 'application is null or undefined'; + const expectedError = 'content is null or undefined'; // act - const act = () => parseApplication(undefined); + const act = () => parseCategoryCollection(undefined); // assert expect(act).to.throw(expectedError); }); describe('actions', () => { it('throws when undefined actions', () => { // arrange - const app = new YamlApplicationBuilder().withActions(undefined).build(); + const expectedError = 'content does not define any action'; + const collection = new YamlApplicationBuilder().withActions(undefined).build(); // act - const act = () => parseApplication(app); + const act = () => parseCategoryCollection(collection); // assert - expect(act).to.throw('application does not define any action'); + expect(act).to.throw(expectedError); }); it('throws when has no actions', () => { // arrange - const app = new YamlApplicationBuilder().withActions([]).build(); + const expectedError = 'content does not define any action'; + const collection = new YamlApplicationBuilder().withActions([]).build(); // act - const act = () => parseApplication(app); + const act = () => parseCategoryCollection(collection); // assert - expect(act).to.throw('application does not define any action'); + expect(act).to.throw(expectedError); }); it('parses actions', () => { // arrange const actions = [ getTestCategory('test1'), getTestCategory('test2') ]; const compiler = new ScriptCompilerStub(); 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 - const actual = parseApplication(app).actions; + const actual = parseCategoryCollection(collection).actions; // assert expect(excludingId(actual)).to.be.deep.equal(excludingId(expected)); function excludingId(array: ReadonlyArray>) { @@ -69,9 +71,9 @@ describe('ApplicationParser', () => { const expected = 'expected-version'; const env = getProcessEnvironmentStub(); env.VUE_APP_VERSION = expected; - const app = new YamlApplicationBuilder().build(); + const collection = new YamlApplicationBuilder().build(); // act - const actual = parseApplication(app, env).info.version; + const actual = parseCategoryCollection(collection, env).info.version; // assert expect(actual).to.be.equal(expected); }); @@ -80,9 +82,9 @@ describe('ApplicationParser', () => { const expected = 'https://expected-repository.url'; const env = getProcessEnvironmentStub(); env.VUE_APP_REPOSITORY_URL = expected; - const app = new YamlApplicationBuilder().build(); + const collection = new YamlApplicationBuilder().build(); // act - const actual = parseApplication(app, env).info.repositoryUrl; + const actual = parseCategoryCollection(collection, env).info.repositoryUrl; // assert expect(actual).to.be.equal(expected); }); @@ -91,9 +93,9 @@ describe('ApplicationParser', () => { const expected = 'expected-app-name'; const env = getProcessEnvironmentStub(); env.VUE_APP_NAME = expected; - const app = new YamlApplicationBuilder().build(); + const collection = new YamlApplicationBuilder().build(); // act - const actual = parseApplication(app, env).info.name; + const actual = parseCategoryCollection(collection, env).info.name; // assert expect(actual).to.be.equal(expected); }); @@ -102,9 +104,9 @@ describe('ApplicationParser', () => { const expected = 'https://expected.sexy'; const env = getProcessEnvironmentStub(); env.VUE_APP_HOMEPAGE_URL = expected; - const app = new YamlApplicationBuilder().build(); + const collection = new YamlApplicationBuilder().build(); // act - const actual = parseApplication(app, env).info.homepage; + const actual = parseCategoryCollection(collection, env).info.homepage; // assert expect(actual).to.be.equal(expected); }); @@ -112,11 +114,11 @@ describe('ApplicationParser', () => { describe('scripting definition', () => { it('parses scripting definition as expected', () => { // arrange - const app = new YamlApplicationBuilder().build(); + const collection = new YamlApplicationBuilder().build(); const information = parseProjectInformation(process.env); - const expected = parseScriptingDefinition(app.scripting, information); + const expected = parseScriptingDefinition(collection.scripting, information); // act - const actual = parseApplication(app).scripting; + const actual = parseCategoryCollection(collection).scripting; // assert expect(expected).to.deep.equal(actual); }); @@ -127,13 +129,13 @@ describe('ApplicationParser', () => { const expectedOs = OperatingSystem.macOS; const osText = 'macos'; const expectedName = 'os'; - const app = new YamlApplicationBuilder() + const collection = new YamlApplicationBuilder() .withOs(osText) .build(); const parserMock = mockEnumParser(expectedName, osText, expectedOs); const env = getProcessEnvironmentStub(); // act - const actual = parseApplication(app, env, parserMock); + const actual = parseCategoryCollection(collection, env, parserMock); // assert expect(actual.os).to.equal(expectedOs); }); diff --git a/tests/unit/domain/Application.spec.ts b/tests/unit/domain/CategoryCollection.spec.ts similarity index 75% rename from tests/unit/domain/Application.spec.ts rename to tests/unit/domain/CategoryCollection.spec.ts index 0bfbd629..265a1321 100644 --- a/tests/unit/domain/Application.spec.ts +++ b/tests/unit/domain/CategoryCollection.spec.ts @@ -1,18 +1,18 @@ -import { ScriptStub } from './../stubs/ScriptStub'; -import { CategoryStub } from './../stubs/CategoryStub'; -import { Application } from '@/domain/Application'; +import { ScriptStub } from '../stubs/ScriptStub'; +import { CategoryStub } from '../stubs/CategoryStub'; import 'mocha'; import { expect } from 'chai'; import { ProjectInformation } from '@/domain/ProjectInformation'; import { IProjectInformation } from '@/domain/IProjectInformation'; -import { ICategory } from '@/domain/IApplication'; +import { ICategory } from '@/domain/ICategory'; import { OperatingSystem } from '@/domain/OperatingSystem'; import { IScriptingDefinition } from '@/domain/IScriptingDefinition'; import { ScriptingLanguage } from '@/domain/ScriptingLanguage'; import { RecommendationLevel } from '@/domain/RecommendationLevel'; import { getEnumValues } from '@/application/Common/Enum'; +import { CategoryCollection } from '../../../src/domain/CategoryCollection'; -describe('Application', () => { +describe('CategoryCollection', () => { describe('getScriptsByLevel', () => { it('filters out scripts without levels', () => { // arrange @@ -25,7 +25,9 @@ describe('Application', () => { const category = new CategoryStub(0) .withScripts(...scriptsWithLevels) .withScript(toIgnore); - const sut = new ApplicationBuilder().withActions([category]).construct(); + const sut = new CategoryCollectionBuilder() + .withActions([category]) + .construct(); // act const actual = sut.getScriptsByLevel(currentLevel); // assert @@ -43,7 +45,9 @@ describe('Application', () => { new CategoryStub(3).withScripts(...expected, new ScriptStub('S3').withLevel(RecommendationLevel.Strict)), ]; - const sut = new ApplicationBuilder().withActions(actions).construct(); + const sut = new CategoryCollectionBuilder() + .withActions(actions) + .construct(); // act const actual = sut.getScriptsByLevel(level); // assert @@ -59,7 +63,9 @@ describe('Application', () => { const actions = [ new CategoryStub(3).withScripts(...expected), ]; - const sut = new ApplicationBuilder().withActions(actions).construct(); + const sut = new CategoryCollectionBuilder() + .withActions(actions) + .construct(); // act const actual = sut.getScriptsByLevel(level); // assert @@ -67,7 +73,8 @@ describe('Application', () => { }); it('throws when level is undefined', () => { // arrange - const sut = new ApplicationBuilder().construct(); + const sut = new CategoryCollectionBuilder() + .construct(); // act const act = () => sut.getScriptsByLevel(undefined); // assert @@ -76,7 +83,8 @@ describe('Application', () => { it('throws when level is out of range', () => { // arrange const invalidValue = 66; - const sut = new ApplicationBuilder().construct(); + const sut = new CategoryCollectionBuilder() + .construct(); // act const act = () => sut.getScriptsByLevel(invalidValue); // assert @@ -89,10 +97,12 @@ describe('Application', () => { const categories = []; // act function construct() { - new ApplicationBuilder().withActions(categories).construct(); + new CategoryCollectionBuilder() + .withActions(categories) + .construct(); } // 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', () => { // arrange @@ -102,10 +112,12 @@ describe('Application', () => { ]; // act function construct() { - new ApplicationBuilder().withActions(categories).construct(); + new CategoryCollectionBuilder() + .withActions(categories) + .construct(); } // 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', () => { // arrange @@ -119,7 +131,7 @@ describe('Application', () => { new ScriptStub(`Script${index}`).withLevel(level), )); // act - const construct = () => new ApplicationBuilder() + const construct = () => new CategoryCollectionBuilder() .withActions(categories) .construct(); // assert @@ -141,7 +153,9 @@ describe('Application', () => { new CategoryStub(4).withScripts(new ScriptStub('S4'))), ]; // act - const sut = new ApplicationBuilder().withActions(categories).construct(); + const sut = new CategoryCollectionBuilder() + .withActions(categories) + .construct(); // assert expect(sut.totalScripts).to.equal(4); }); @@ -156,7 +170,7 @@ describe('Application', () => { new CategoryStub(3).withCategories(new CategoryStub(4).withScripts(new ScriptStub('S4'))), ]; // act - const sut = new ApplicationBuilder() + const sut = new CategoryCollectionBuilder() .withActions(categories) .construct(); // assert @@ -169,7 +183,9 @@ describe('Application', () => { const expected = new ProjectInformation( 'expected-name', 'expected-repo', '0.31.0', 'expected-homepage'); // act - const sut = new ApplicationBuilder().withInfo(expected).construct(); + const sut = new CategoryCollectionBuilder() + .withInfo(expected) + .construct(); // assert expect(sut.info).to.deep.equal(expected); }); @@ -178,7 +194,9 @@ describe('Application', () => { const information = undefined; // act function construct() { - return new ApplicationBuilder().withInfo(information).construct(); + return new CategoryCollectionBuilder() + .withInfo(information) + .construct(); } // assert expect(construct).to.throw('undefined info'); @@ -189,7 +207,9 @@ describe('Application', () => { // arrange const expected = OperatingSystem.macOS; // act - const sut = new ApplicationBuilder().withOs(expected).construct(); + const sut = new CategoryCollectionBuilder() + .withOs(expected) + .construct(); // assert expect(sut.os).to.deep.equal(expected); }); @@ -197,7 +217,9 @@ describe('Application', () => { // arrange const os = OperatingSystem.Unknown; // act - const construct = () => new ApplicationBuilder().withOs(os).construct(); + const construct = () => new CategoryCollectionBuilder() + .withOs(os) + .construct(); // assert expect(construct).to.throw('unknown os'); }); @@ -205,7 +227,9 @@ describe('Application', () => { // arrange const os = undefined; // act - const construct = () => new ApplicationBuilder().withOs(os).construct(); + const construct = () => new CategoryCollectionBuilder() + .withOs(os) + .construct(); // assert expect(construct).to.throw('undefined os'); }); @@ -213,7 +237,9 @@ describe('Application', () => { // arrange const os: OperatingSystem = 666; // act - const construct = () => new ApplicationBuilder().withOs(os).construct(); + const construct = () => new CategoryCollectionBuilder() + .withOs(os) + .construct(); // assert expect(construct).to.throw(`os "${os}" is out of range`); }); @@ -223,7 +249,9 @@ describe('Application', () => { // arrange const expected = getValidScriptingDefinition(); // act - const sut = new ApplicationBuilder().withScripting(expected).construct(); + const sut = new CategoryCollectionBuilder() + .withScripting(expected) + .construct(); // assert expect(sut.scripting).to.deep.equal(expected); }); @@ -232,7 +260,9 @@ describe('Application', () => { const scriptingDefinition = undefined; // act function construct() { - return new ApplicationBuilder().withScripting(scriptingDefinition).construct(); + return new CategoryCollectionBuilder() + .withScripting(scriptingDefinition) + .construct(); } // assert expect(construct).to.throw('undefined scripting definition'); @@ -249,7 +279,7 @@ function getValidScriptingDefinition(): IScriptingDefinition { }; } -class ApplicationBuilder { +class CategoryCollectionBuilder { private os = OperatingSystem.Windows; private info = new ProjectInformation('name', 'repo', '0.1.0', 'homepage'); private actions: readonly ICategory[] = [ @@ -258,23 +288,23 @@ class ApplicationBuilder { new ScriptStub('S2').withLevel(RecommendationLevel.Strict)), ]; private script: IScriptingDefinition = getValidScriptingDefinition(); - public withOs(os: OperatingSystem): ApplicationBuilder { + public withOs(os: OperatingSystem): CategoryCollectionBuilder { this.os = os; return this; } - public withInfo(info: IProjectInformation) { + public withInfo(info: IProjectInformation): CategoryCollectionBuilder { this.info = info; return this; } - public withActions(actions: readonly ICategory[]) { + public withActions(actions: readonly ICategory[]): CategoryCollectionBuilder { this.actions = actions; return this; } - public withScripting(script: IScriptingDefinition) { + public withScripting(script: IScriptingDefinition): CategoryCollectionBuilder { this.script = script; return this; } - public construct(): Application { - return new Application(this.os, this.info, this.actions, this.script); + public construct(): CategoryCollection { + return new CategoryCollection(this.os, this.info, this.actions, this.script); } } diff --git a/tests/unit/presentation/Scripts/ScriptsTree/ScriptNodeParser.spec.ts b/tests/unit/presentation/Scripts/ScriptsTree/ScriptNodeParser.spec.ts index 5c2920cb..51c0e093 100644 --- a/tests/unit/presentation/Scripts/ScriptsTree/ScriptNodeParser.spec.ts +++ b/tests/unit/presentation/Scripts/ScriptsTree/ScriptNodeParser.spec.ts @@ -4,7 +4,7 @@ import { getScriptNodeId, getScriptId, getCategoryNodeId, getCategoryId } from ' import { CategoryStub } from '../../../stubs/CategoryStub'; import { ScriptStub } from '../../../stubs/ScriptStub'; 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 { IScript } from '@/domain/IScript'; import { ICategory } from '@/domain/ICategory'; @@ -36,11 +36,11 @@ describe('ScriptNodeParser', () => { const secondSubCategory = new CategoryStub(categoryId) .withCategory(new CategoryStub(33).withScriptIds('331', '331')) .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(secondSubCategory)); // act - const nodes = parseSingleCategory(categoryId, app); + const nodes = parseSingleCategory(categoryId, collection); // assert expect(nodes).to.have.lengthOf(2); expectSameCategory(nodes[0], firstSubCategory); @@ -50,9 +50,10 @@ describe('ScriptNodeParser', () => { // arrange const categoryId = 31; 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 - const nodes = parseSingleCategory(categoryId, app); + const nodes = parseSingleCategory(categoryId, collection); // assert expect(nodes).to.have.lengthOf(3); expectSameScript(nodes[0], scripts[0]); @@ -63,18 +64,18 @@ describe('ScriptNodeParser', () => { it('parseAllCategories parses as expected', () => { // arrange - const app = new ApplicationStub() + const collection = new CategoryCollectionStub() .withAction(new CategoryStub(0).withScriptIds('1, 2')) .withAction(new CategoryStub(1).withCategories( new CategoryStub(3).withScriptIds('3', '4'), new CategoryStub(4).withCategory(new CategoryStub(5).withScriptIds('6')), )); // act - const nodes = parseAllCategories(app); + const nodes = parseAllCategories(collection); // assert expect(nodes).to.have.lengthOf(2); - expectSameCategory(nodes[0], app.actions[0]); - expectSameCategory(nodes[1], app.actions[1]); + expectSameCategory(nodes[0], collection.actions[0]); + expectSameCategory(nodes[1], collection.actions[1]); }); }); diff --git a/tests/unit/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/CategoryReverter.spec.ts b/tests/unit/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/CategoryReverter.spec.ts index b46ae8a3..b680d65c 100644 --- a/tests/unit/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/CategoryReverter.spec.ts +++ b/tests/unit/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/CategoryReverter.spec.ts @@ -4,7 +4,7 @@ import { ScriptStub } from '../../../../../../stubs/ScriptStub'; import { CategoryReverter } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/CategoryReverter'; import { getCategoryNodeId } from '@/presentation/Scripts/ScriptsTree/ScriptNodeParser'; import { CategoryStub } from '../../../../../../stubs/CategoryStub'; -import { ApplicationStub } from '../../../../../../stubs/ApplicationStub'; +import { CategoryCollectionStub } from '../../../../../../stubs/CategoryCollectionStub'; import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript'; import { UserSelection } from '@/application/Context/State/Selection/UserSelection'; @@ -17,8 +17,8 @@ describe('CategoryReverter', () => { ]; const category = new CategoryStub(1).withScripts(...scripts); const nodeId = getCategoryNodeId(category); - const app = new ApplicationStub().withAction(category); - const sut = new CategoryReverter(nodeId, app); + const collection = new CategoryCollectionStub().withAction(category); + const sut = new CategoryReverter(nodeId, collection); const testCases = [ { name: 'false when subscripts are not reverted', @@ -52,7 +52,7 @@ describe('CategoryReverter', () => { new ScriptStub('revertable2').withRevertCode('REM revert me 2'), ]; const category = new CategoryStub(1).withScripts(...scripts); - const app = new ApplicationStub().withAction(category); + const collection = new CategoryCollectionStub().withAction(category); const testCases = [ { name: 'selects with revert state when not selected', @@ -88,8 +88,8 @@ describe('CategoryReverter', () => { const nodeId = getCategoryNodeId(category); for (const testCase of testCases) { it(testCase.name, () => { - const selection = new UserSelection(app, testCase.selection); - const sut = new CategoryReverter(nodeId, app); + const selection = new UserSelection(collection, testCase.selection); + const sut = new CategoryReverter(nodeId, collection); // act sut.selectWithRevertState(testCase.revert, selection); // assert diff --git a/tests/unit/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ReverterFactory.spec.ts b/tests/unit/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ReverterFactory.spec.ts index 415dcd2f..0b4d983d 100644 --- a/tests/unit/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ReverterFactory.spec.ts +++ b/tests/unit/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ReverterFactory.spec.ts @@ -4,7 +4,7 @@ import { INode, NodeType } from '@/presentation/Scripts/ScriptsTree/SelectableTr import { getReverter } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ReverterFactory'; import { ScriptReverter } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ScriptReverter'; 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 { getScriptNodeId, getCategoryNodeId } from '@/presentation/Scripts/ScriptsTree/ScriptNodeParser'; import { ScriptStub } from '../../../../../../stubs/ScriptStub'; @@ -15,9 +15,10 @@ describe('ReverterFactory', () => { // arrange const category = new CategoryStub(0).withScriptIds('55'); const node = getNodeStub(getCategoryNodeId(category), NodeType.Category); - const app = new ApplicationStub().withAction(category); + const collection = new CategoryCollectionStub() + .withAction(category); // act - const result = getReverter(node, app); + const result = getReverter(node, collection); // assert expect(result instanceof CategoryReverter).to.equal(true); }); @@ -25,9 +26,10 @@ describe('ReverterFactory', () => { // arrange const script = new ScriptStub('test'); 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 - const result = getReverter(node, app); + const result = getReverter(node, collection); // assert expect(result instanceof ScriptReverter).to.equal(true); }); diff --git a/tests/unit/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ScriptReverter.spec.ts b/tests/unit/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ScriptReverter.spec.ts index 9f6468d5..3e281e29 100644 --- a/tests/unit/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ScriptReverter.spec.ts +++ b/tests/unit/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ScriptReverter.spec.ts @@ -1,13 +1,13 @@ import 'mocha'; import { expect } from 'chai'; import { ScriptReverter } from '@/presentation/Scripts/ScriptsTree/SelectableTree/Node/Reverter/ScriptReverter'; -import { SelectedScriptStub } from '../../../../../../stubs/SelectedScriptStub'; import { getScriptNodeId } from '@/presentation/Scripts/ScriptsTree/ScriptNodeParser'; -import { ScriptStub } from '../../../../../../stubs/ScriptStub'; import { UserSelection } from '@/application/Context/State/Selection/UserSelection'; import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript'; -import { ApplicationStub } from '../../../../../../stubs/ApplicationStub'; +import { CategoryCollectionStub } from '../../../../../../stubs/CategoryCollectionStub'; import { CategoryStub } from '../../../../../../stubs/CategoryStub'; +import { ScriptStub } from '../../../../../../stubs/ScriptStub'; +import { SelectedScriptStub } from '../../../../../../stubs/SelectedScriptStub'; describe('ScriptReverter', () => { describe('getState', () => { @@ -45,7 +45,8 @@ describe('ScriptReverter', () => { describe('selectWithRevertState', () => { // arrange 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 = [ { name: 'selects with revert state when not selected', @@ -75,7 +76,7 @@ describe('ScriptReverter', () => { const nodeId = getScriptNodeId(script); for (const testCase of testCases) { it(testCase.name, () => { - const selection = new UserSelection(app, testCase.selection); + const selection = new UserSelection(collection, testCase.selection); const sut = new ScriptReverter(nodeId); // act sut.selectWithRevertState(testCase.revert, selection); diff --git a/tests/unit/stubs/ApplicationStub.ts b/tests/unit/stubs/CategoryCollectionStub.ts similarity index 85% rename from tests/unit/stubs/ApplicationStub.ts rename to tests/unit/stubs/CategoryCollectionStub.ts index e7ab9e9b..67a299a6 100644 --- a/tests/unit/stubs/ApplicationStub.ts +++ b/tests/unit/stubs/CategoryCollectionStub.ts @@ -1,11 +1,13 @@ import { ScriptingDefinitionStub } from './ScriptingDefinitionStub'; -import { IApplication, ICategory, IScript } from '@/domain/IApplication'; import { ProjectInformation } from '@/domain/ProjectInformation'; import { OperatingSystem } from '@/domain/OperatingSystem'; import { ScriptStub } from './ScriptStub'; 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 os = OperatingSystem.Linux; 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 actions = new Array(); - public withAction(category: ICategory): ApplicationStub { + public withAction(category: ICategory): CategoryCollectionStub { this.actions.push(category); return this; } - public withOs(os: OperatingSystem): ApplicationStub { + public withOs(os: OperatingSystem): CategoryCollectionStub { this.os = os; return this; } - public withScripting(scripting: IScriptingDefinition): ApplicationStub { + public withScripting(scripting: IScriptingDefinition): CategoryCollectionStub { this.scripting = scripting; return this; } - public withInitialScript(script: IScript): ApplicationStub { + public withInitialScript(script: IScript): CategoryCollectionStub { this.initialScript = script; return this; }