Refactor to remove "Async" function name suffix

Remove convention where Async suffix is added to functions that returns
a Promise. It was a habit from C#, but is not widely used in JavaScript
/ TypeScript world, also bloats the code. The code is more consistent
with third party dependencies/frameworks without the suffix.
This commit is contained in:
undergroundwires
2021-10-30 12:35:20 +01:00
parent 799fb091b8
commit 82c43ba2e3
35 changed files with 189 additions and 187 deletions

View File

@@ -15,7 +15,7 @@ export class ApplicationFactory implements IApplicationFactory {
} }
this.getter = new AsyncLazy<IApplication>(() => Promise.resolve(costlyGetter())); this.getter = new AsyncLazy<IApplication>(() => Promise.resolve(costlyGetter()));
} }
public getAppAsync(): Promise<IApplication> { public getApp(): Promise<IApplication> {
return this.getter.getValueAsync(); return this.getter.getValue();
} }
} }

View File

@@ -7,12 +7,12 @@ import { IEnvironment } from '../Environment/IEnvironment';
import { IApplicationFactory } from '../IApplicationFactory'; import { IApplicationFactory } from '../IApplicationFactory';
import { ApplicationFactory } from '../ApplicationFactory'; import { ApplicationFactory } from '../ApplicationFactory';
export async function buildContextAsync( export async function buildContext(
factory: IApplicationFactory = ApplicationFactory.Current, factory: IApplicationFactory = ApplicationFactory.Current,
environment = Environment.CurrentEnvironment): Promise<IApplicationContext> { environment = Environment.CurrentEnvironment): Promise<IApplicationContext> {
if (!factory) { throw new Error('undefined factory'); } if (!factory) { throw new Error('undefined factory'); }
if (!environment) { throw new Error('undefined environment'); } if (!environment) { throw new Error('undefined environment'); }
const app = await factory.getAppAsync(); const app = await factory.getApp();
const os = getInitialOs(app, environment); const os = getInitialOs(app, environment);
return new ApplicationContext(app, os); return new ApplicationContext(app, os);
} }

View File

@@ -1,5 +1,5 @@
import { IApplication } from '@/domain/IApplication'; import { IApplication } from '@/domain/IApplication';
export interface IApplicationFactory { export interface IApplicationFactory {
getAppAsync(): Promise<IApplication>; getApp(): Promise<IApplication>;
} }

View File

@@ -10,7 +10,7 @@ export class CodeRunner {
private readonly node = getNodeJs(), private readonly node = getNodeJs(),
private readonly environment = Environment.CurrentEnvironment) { private readonly environment = Environment.CurrentEnvironment) {
} }
public async runCodeAsync(code: string, folderName: string, fileExtension: string): Promise<void> { public async runCode(code: string, folderName: string, fileExtension: string): Promise<void> {
const dir = this.node.path.join(this.node.os.tmpdir(), folderName); const dir = this.node.path.join(this.node.os.tmpdir(), folderName);
await this.node.fs.promises.mkdir(dir, {recursive: true}); await this.node.fs.promises.mkdir(dir, {recursive: true});
const filePath = this.node.path.join(dir, `run.${fileExtension}`); const filePath = this.node.path.join(dir, `run.${fileExtension}`);

View File

@@ -12,7 +12,7 @@ export class AsyncLazy<T> {
this.valueFactory = valueFactory; this.valueFactory = valueFactory;
} }
public async getValueAsync(): Promise<T> { public async getValue(): Promise<T> {
// If value is already created, return the value directly // If value is already created, return the value directly
if (this.isValueCreated) { if (this.isValueCreated) {
return Promise.resolve(this.value); return Promise.resolve(this.value);

View File

@@ -1,5 +1,5 @@
export type SchedulerType = (callback: (...args: any[]) => void, ms: number) => void; export type SchedulerType = (callback: (...args: any[]) => void, ms: number) => void;
export function sleepAsync(time: number, scheduler: SchedulerType = setTimeout) { export function sleep(time: number, scheduler: SchedulerType = setTimeout) {
return new Promise((resolve) => scheduler(() => resolve(undefined), time)); return new Promise((resolve) => scheduler(() => resolve(undefined), time));
} }

View File

@@ -96,7 +96,7 @@ export default class MacOsInstructions extends Vue {
public macOsDownloadUrl = ''; public macOsDownloadUrl = '';
public async created() { public async created() {
const app = await ApplicationFactory.Current.getAppAsync(); const app = await ApplicationFactory.Current.getApp();
this.appName = app.info.name; this.appName = app.info.name;
this.macOsDownloadUrl = app.info.getDownloadUrl(OperatingSystem.macOS); this.macOsDownloadUrl = app.info.getDownloadUrl(OperatingSystem.macOS);
} }

View File

@@ -3,18 +3,18 @@
<IconButton <IconButton
v-if="this.canRun" v-if="this.canRun"
text="Run" text="Run"
v-on:click="executeCodeAsync" v-on:click="executeCode"
icon-prefix="fas" icon-name="play"> icon-prefix="fas" icon-name="play">
</IconButton> </IconButton>
<IconButton <IconButton
:text="this.isDesktopVersion ? 'Save' : 'Download'" :text="this.isDesktopVersion ? 'Save' : 'Download'"
v-on:click="saveCodeAsync" v-on:click="saveCode"
icon-prefix="fas" icon-prefix="fas"
:icon-name="this.isDesktopVersion ? 'save' : 'file-download'"> :icon-name="this.isDesktopVersion ? 'save' : 'file-download'">
</IconButton> </IconButton>
<IconButton <IconButton
text="Copy" text="Copy"
v-on:click="copyCodeAsync" v-on:click="copyCode"
icon-prefix="fas" icon-name="copy"> icon-prefix="fas" icon-name="copy">
</IconButton> </IconButton>
<Dialog v-if="this.isMacOsCollection" ref="instructionsDialog"> <Dialog v-if="this.isMacOsCollection" ref="instructionsDialog">
@@ -54,20 +54,20 @@ export default class TheCodeButtons extends StatefulVue {
public isMacOsCollection = false; public isMacOsCollection = false;
public fileName = ''; public fileName = '';
public async copyCodeAsync() { public async copyCode() {
const code = await this.getCurrentCodeAsync(); const code = await this.getCurrentCode();
Clipboard.copyText(code.current); Clipboard.copyText(code.current);
} }
public async saveCodeAsync() { public async saveCode() {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContext();
saveCode(this.fileName, context.state); saveCode(this.fileName, context.state);
if (this.isMacOsCollection) { if (this.isMacOsCollection) {
(this.$refs.instructionsDialog as any).show(); (this.$refs.instructionsDialog as any).show();
} }
} }
public async executeCodeAsync() { public async executeCode() {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContext();
await executeCodeAsync(context); await executeCode(context);
} }
protected handleCollectionState(newState: ICategoryCollectionState): void { protected handleCollectionState(newState: ICategoryCollectionState): void {
@@ -77,8 +77,8 @@ export default class TheCodeButtons extends StatefulVue {
this.react(newState.code); this.react(newState.code);
} }
private async getCurrentCodeAsync(): Promise<IApplicationCode> { private async getCurrentCode(): Promise<IApplicationCode> {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContext();
const code = context.state.code; const code = context.state.code;
return code; return code;
} }
@@ -115,9 +115,9 @@ function buildFileName(scripting: IScriptingDefinition) {
return fileName; return fileName;
} }
async function executeCodeAsync(context: IApplicationContext) { async function executeCode(context: IApplicationContext) {
const runner = new CodeRunner(); const runner = new CodeRunner();
await runner.runCodeAsync( await runner.runCode(
/*code*/ context.state.code.current, /*code*/ context.state.code.current,
/*appName*/ context.app.info.name, /*appName*/ context.app.info.name,
/*fileExtension*/ context.state.collection.scripting.fileExtension, /*fileExtension*/ context.state.collection.scripting.fileExtension,

View File

@@ -51,13 +51,13 @@ export default class TheCodeArea extends StatefulVue {
const appCode = newState.code; const appCode = newState.code;
this.editor.setValue(appCode.current || getDefaultCode(newState.collection.scripting.language), 1); this.editor.setValue(appCode.current || getDefaultCode(newState.collection.scripting.language), 1);
this.events.unsubscribeAll(); this.events.unsubscribeAll();
this.events.register(appCode.changed.on((code) => this.updateCodeAsync(code))); this.events.register(appCode.changed.on((code) => this.updateCode(code)));
} }
private async updateCodeAsync(event: ICodeChangedEvent) { private async updateCode(event: ICodeChangedEvent) {
this.removeCurrentHighlighting(); this.removeCurrentHighlighting();
if (event.isEmpty()) { if (event.isEmpty()) {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContext();
const defaultCode = getDefaultCode(context.state.collection.scripting.language); const defaultCode = getDefaultCode(context.state.collection.scripting.language);
this.editor.setValue(defaultCode, 1); this.editor.setValue(defaultCode, 1);
return; return;

View File

@@ -3,7 +3,7 @@
<MenuOptionListItem <MenuOptionListItem
v-for="os in this.allOses" :key="os.name" v-for="os in this.allOses" :key="os.name"
:enabled="currentOs !== os.os" :enabled="currentOs !== os.os"
@click="changeOsAsync(os.os)" @click="changeOs(os.os)"
:label="os.name" :label="os.name"
/> />
</MenuOptionList> </MenuOptionList>
@@ -29,12 +29,12 @@ export default class TheOsChanger extends StatefulVue {
public currentOs?: OperatingSystem = null; public currentOs?: OperatingSystem = null;
public async created() { public async created() {
const app = await ApplicationFactory.Current.getAppAsync(); const app = await ApplicationFactory.Current.getApp();
this.allOses = app.getSupportedOsList() this.allOses = app.getSupportedOsList()
.map((os) => ({ os, name: renderOsName(os) })); .map((os) => ({ os, name: renderOsName(os) }));
} }
public async changeOsAsync(newOs: OperatingSystem) { public async changeOs(newOs: OperatingSystem) {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContext();
context.changeContext(newOs); context.changeContext(newOs);
} }

View File

@@ -50,10 +50,10 @@ export default class CardListItem extends StatefulVue {
public areAllChildrenSelected = false; public areAllChildrenSelected = false;
public async mounted() { public async mounted() {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContext();
this.events.register(context.state.selection.changed.on( this.events.register(context.state.selection.changed.on(
() => this.updateSelectionIndicatorsAsync(this.categoryId))); () => this.updateSelectionIndicators(this.categoryId)));
await this.updateStateAsync(this.categoryId); await this.updateState(this.categoryId);
} }
@Emit('selected') @Emit('selected')
public onSelected(isExpanded: boolean) { public onSelected(isExpanded: boolean) {
@@ -64,7 +64,7 @@ export default class CardListItem extends StatefulVue {
this.isExpanded = value === this.categoryId; this.isExpanded = value === this.categoryId;
} }
@Watch('isExpanded') @Watch('isExpanded')
public async onExpansionChangedAsync(newValue: number, oldValue: number) { public async onExpansionChanged(newValue: number, oldValue: number) {
if (!oldValue && newValue) { if (!oldValue && newValue) {
await new Promise((r) => setTimeout(r, 400)); await new Promise((r) => setTimeout(r, 400));
const focusElement = this.$refs.cardElement as HTMLElement; const focusElement = this.$refs.cardElement as HTMLElement;
@@ -72,19 +72,19 @@ export default class CardListItem extends StatefulVue {
} }
} }
@Watch('categoryId') @Watch('categoryId')
public async updateStateAsync(value: |number) { public async updateState(value: |number) {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContext();
const category = !value ? undefined : context.state.collection.findCategory(value); const category = !value ? undefined : context.state.collection.findCategory(value);
this.cardTitle = category ? category.name : undefined; this.cardTitle = category ? category.name : undefined;
await this.updateSelectionIndicatorsAsync(value); await this.updateSelectionIndicators(value);
} }
protected handleCollectionState(): void { protected handleCollectionState(): void {
return; return;
} }
private async updateSelectionIndicatorsAsync(categoryId: number) { private async updateSelectionIndicators(categoryId: number) {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContext();
const selection = context.state.selection; const selection = context.state.selection;
const category = context.state.collection.findCategory(categoryId); const category = context.state.collection.findCategory(categoryId);
this.isAnyChildSelected = category ? selection.isAnySelected(category) : false; this.isAnyChildSelected = category ? selection.isAnySelected(category) : false;

View File

@@ -6,7 +6,7 @@
:selectedNodeIds="selectedNodeIds" :selectedNodeIds="selectedNodeIds"
:filterPredicate="filterPredicate" :filterPredicate="filterPredicate"
:filterText="filterText" :filterText="filterText"
v-on:nodeSelected="toggleNodeSelectionAsync($event)" v-on:nodeSelected="toggleNodeSelection($event)"
> >
</SelectableTree> </SelectableTree>
</span> </span>
@@ -42,8 +42,8 @@ export default class ScriptsTree extends StatefulVue {
private filtered?: IFilterResult; private filtered?: IFilterResult;
public async toggleNodeSelectionAsync(event: INodeSelectedEvent) { public async toggleNodeSelection(event: INodeSelectedEvent) {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContext();
switch (event.node.type) { switch (event.node.type) {
case NodeType.Category: case NodeType.Category:
toggleCategoryNodeSelection(event, context.state); toggleCategoryNodeSelection(event, context.state);
@@ -56,8 +56,8 @@ export default class ScriptsTree extends StatefulVue {
} }
} }
@Watch('categoryId', { immediate: true }) @Watch('categoryId', { immediate: true })
public async setNodesAsync(categoryId?: number) { public async setNodes(categoryId?: number) {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContext();
if (categoryId) { if (categoryId) {
this.nodes = parseSingleCategory(categoryId, context.state.collection); this.nodes = parseSingleCategory(categoryId, context.state.collection);
} else { } else {

View File

@@ -2,7 +2,7 @@
<div class="checkbox-switch" > <div class="checkbox-switch" >
<input type="checkbox" class="input-checkbox" <input type="checkbox" class="input-checkbox"
v-model="isReverted" v-model="isReverted"
@change="onRevertToggledAsync()" @change="onRevertToggled()"
v-on:click.stop> v-on:click.stop>
<div class="checkbox-animate"> <div class="checkbox-animate">
<span class="checkbox-off">revert</span> <span class="checkbox-off">revert</span>
@@ -28,12 +28,12 @@ export default class RevertToggle extends StatefulVue {
private handler: IReverter; private handler: IReverter;
@Watch('node', {immediate: true}) public async onNodeChangedAsync(node: INode) { @Watch('node', {immediate: true}) public async onNodeChanged(node: INode) {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContext();
this.handler = getReverter(node, context.state.collection); this.handler = getReverter(node, context.state.collection);
} }
public async onRevertToggledAsync() { public async onRevertToggled() {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContext();
this.handler.selectWithRevertState(this.isReverted, context.state.selection); this.handler.selectWithRevertState(this.isReverted, context.state.selection);
} }

View File

@@ -23,7 +23,7 @@ import Node from './Node/Node.vue';
import { INode } from './Node/INode'; import { INode } from './Node/INode';
import { convertExistingToNode, toNewLiquorTreeNode } from './LiquorTree/NodeWrapper/NodeTranslator'; import { convertExistingToNode, toNewLiquorTreeNode } from './LiquorTree/NodeWrapper/NodeTranslator';
import { INodeSelectedEvent } from './INodeSelectedEvent'; import { INodeSelectedEvent } from './INodeSelectedEvent';
import { sleepAsync } from '@/infrastructure/Threading/AsyncSleep'; import { sleep } from '@/infrastructure/Threading/AsyncSleep';
import { getNewState } from './LiquorTree/NodeWrapper/NodeStateUpdater'; import { getNewState } from './LiquorTree/NodeWrapper/NodeStateUpdater';
import { LiquorTreeOptions } from './LiquorTree/LiquorTreeOptions'; import { LiquorTreeOptions } from './LiquorTree/LiquorTreeOptions';
import { FilterPredicate, NodePredicateFilter } from './LiquorTree/NodeWrapper/NodePredicateFilter'; import { FilterPredicate, NodePredicateFilter } from './LiquorTree/NodeWrapper/NodePredicateFilter';
@@ -56,7 +56,7 @@ export default class SelectableTree extends Vue { // Keep it stateless to make i
} }
@Watch('initialNodes', { immediate: true }) @Watch('initialNodes', { immediate: true })
public async updateNodesAsync(nodes: readonly INode[]) { public async updateNodes(nodes: readonly INode[]) {
if (!nodes) { if (!nodes) {
throw new Error('undefined initial nodes'); throw new Error('undefined initial nodes');
} }
@@ -66,12 +66,12 @@ export default class SelectableTree extends Vue { // Keep it stateless to make i
(node) => node.state = updateState(node.state, node, this.selectedNodeIds)); (node) => node.state = updateState(node.state, node, this.selectedNodeIds));
} }
this.initialLiquourTreeNodes = initialNodes; this.initialLiquourTreeNodes = initialNodes;
const api = await this.getLiquorTreeApiAsync(); const api = await this.getLiquorTreeApi();
api.setModel(this.initialLiquourTreeNodes); // as liquor tree is not reactive to data after initialization api.setModel(this.initialLiquourTreeNodes); // as liquor tree is not reactive to data after initialization
} }
@Watch('filterText', { immediate: true }) @Watch('filterText', { immediate: true })
public async updateFilterTextAsync(filterText: |string) { public async updateFilterText(filterText: |string) {
const api = await this.getLiquorTreeApiAsync(); const api = await this.getLiquorTreeApi();
if (!filterText) { if (!filterText) {
api.clearFilter(); api.clearFilter();
} else { } else {
@@ -80,22 +80,22 @@ export default class SelectableTree extends Vue { // Keep it stateless to make i
} }
@Watch('selectedNodeIds') @Watch('selectedNodeIds')
public async setSelectedStatusAsync(selectedNodeIds: ReadonlyArray<string>) { public async setSelectedStatus(selectedNodeIds: ReadonlyArray<string>) {
if (!selectedNodeIds) { if (!selectedNodeIds) {
throw new Error('SelectedrecurseDown nodes are undefined'); throw new Error('SelectedrecurseDown nodes are undefined');
} }
const tree = await this.getLiquorTreeApiAsync(); const tree = await this.getLiquorTreeApi();
tree.recurseDown( tree.recurseDown(
(node) => node.states = updateState(node.states, node, selectedNodeIds), (node) => node.states = updateState(node.states, node, selectedNodeIds),
); );
} }
private async getLiquorTreeApiAsync(): Promise<ILiquorTree> { private async getLiquorTreeApi(): Promise<ILiquorTree> {
const accessor = (): ILiquorTree => { const accessor = (): ILiquorTree => {
const uiElement = this.$refs.treeElement; const uiElement = this.$refs.treeElement;
return uiElement ? (uiElement as any).tree : undefined; return uiElement ? (uiElement as any).tree : undefined;
}; };
const treeElement = await tryUntilDefinedAsync(accessor, 5, 20); // Wait for it to render const treeElement = await tryUntilDefined(accessor, 5, 20); // Wait for it to render
if (!treeElement) { if (!treeElement) {
throw Error('Referenced tree element cannot be found. Perhaps it\'s not yet rendered?'); throw Error('Referenced tree element cannot be found. Perhaps it\'s not yet rendered?');
} }
@@ -119,7 +119,7 @@ function recurseDown(
} }
} }
} }
async function tryUntilDefinedAsync<T>( async function tryUntilDefined<T>(
accessor: () => T | undefined, accessor: () => T | undefined,
delayInMs: number, maxTries: number): Promise<T | undefined> { delayInMs: number, maxTries: number): Promise<T | undefined> {
let triesLeft = maxTries; let triesLeft = maxTries;
@@ -130,7 +130,7 @@ async function tryUntilDefinedAsync<T>(
return value; return value;
} }
triesLeft--; triesLeft--;
await sleepAsync(delayInMs); await sleep(delayInMs);
} }
return value; return value;
} }

View File

@@ -13,7 +13,7 @@
<div class="search__query__close-button"> <div class="search__query__close-button">
<font-awesome-icon <font-awesome-icon
:icon="['fas', 'times']" :icon="['fas', 'times']"
v-on:click="clearSearchQueryAsync()"/> v-on:click="clearSearchQuery()"/>
</div> </div>
</div> </div>
<div v-if="!searchHasMatches" class="search-no-matches"> <div v-if="!searchHasMatches" class="search-no-matches">
@@ -65,11 +65,11 @@ export default class TheScriptsView extends StatefulVue {
public ViewType = ViewType; // Make it accessible from the view public ViewType = ViewType; // Make it accessible from the view
public async created() { public async created() {
const app = await ApplicationFactory.Current.getAppAsync(); const app = await ApplicationFactory.Current.getApp();
this.repositoryUrl = app.info.repositoryWebUrl; this.repositoryUrl = app.info.repositoryWebUrl;
} }
public async clearSearchQueryAsync() { public async clearSearchQuery() {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContext();
const filter = context.state.filter; const filter = context.state.filter;
filter.removeFilter(); filter.removeFilter();
} }

View File

@@ -1,7 +1,7 @@
import { Component, Vue } from 'vue-property-decorator'; import { Component, Vue } from 'vue-property-decorator';
import { AsyncLazy } from '@/infrastructure/Threading/AsyncLazy'; import { AsyncLazy } from '@/infrastructure/Threading/AsyncLazy';
import { IApplicationContext } from '@/application/Context/IApplicationContext'; import { IApplicationContext } from '@/application/Context/IApplicationContext';
import { buildContextAsync } from '@/application/Context/ApplicationContextFactory'; import { buildContext } from '@/application/Context/ApplicationContextFactory';
import { IApplicationContextChangedEvent } from '@/application/Context/IApplicationContext'; import { IApplicationContextChangedEvent } from '@/application/Context/IApplicationContext';
import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
import { EventSubscriptionCollection } from '@/infrastructure/Events/EventSubscriptionCollection'; import { EventSubscriptionCollection } from '@/infrastructure/Events/EventSubscriptionCollection';
@@ -9,14 +9,14 @@ import { EventSubscriptionCollection } from '@/infrastructure/Events/EventSubscr
// @ts-ignore because https://github.com/vuejs/vue-class-component/issues/91 // @ts-ignore because https://github.com/vuejs/vue-class-component/issues/91
@Component @Component
export abstract class StatefulVue extends Vue { export abstract class StatefulVue extends Vue {
private static readonly instance = new AsyncLazy<IApplicationContext>(() => buildContextAsync()); private static readonly instance = new AsyncLazy<IApplicationContext>(() => buildContext());
protected readonly events = new EventSubscriptionCollection(); protected readonly events = new EventSubscriptionCollection();
private readonly ownEvents = new EventSubscriptionCollection(); private readonly ownEvents = new EventSubscriptionCollection();
public async mounted() { public async mounted() {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContext();
this.ownEvents.register(context.contextChanged.on((event) => this.handleStateChangedEvent(event))); this.ownEvents.register(context.contextChanged.on((event) => this.handleStateChangedEvent(event)));
this.handleCollectionState(context.state, undefined); this.handleCollectionState(context.state, undefined);
} }
@@ -27,8 +27,8 @@ export abstract class StatefulVue extends Vue {
protected abstract handleCollectionState( protected abstract handleCollectionState(
newState: ICategoryCollectionState, oldState: ICategoryCollectionState | undefined): void; newState: ICategoryCollectionState, oldState: ICategoryCollectionState | undefined): void;
protected getCurrentContextAsync(): Promise<IApplicationContext> { protected getCurrentContext(): Promise<IApplicationContext> {
return StatefulVue.instance.getValueAsync(); return StatefulVue.instance.getValue();
} }
private handleStateChangedEvent(event: IApplicationContextChangedEvent) { private handleStateChangedEvent(event: IApplicationContextChangedEvent) {

View File

@@ -24,20 +24,20 @@ export default class DownloadUrlListItem extends Vue {
public hasCurrentOsDesktopVersion: boolean = false; public hasCurrentOsDesktopVersion: boolean = false;
public async mounted() { public async mounted() {
await this.onOperatingSystemChangedAsync(this.operatingSystem); await this.onOperatingSystemChanged(this.operatingSystem);
} }
@Watch('operatingSystem') @Watch('operatingSystem')
public async onOperatingSystemChangedAsync(os: OperatingSystem) { public async onOperatingSystemChanged(os: OperatingSystem) {
const currentOs = Environment.CurrentEnvironment.os; const currentOs = Environment.CurrentEnvironment.os;
this.isCurrentOs = os === currentOs; this.isCurrentOs = os === currentOs;
this.downloadUrl = await this.getDownloadUrlAsync(os); this.downloadUrl = await this.getDownloadUrl(os);
this.operatingSystemName = getOperatingSystemName(os); this.operatingSystemName = getOperatingSystemName(os);
this.hasCurrentOsDesktopVersion = hasDesktopVersion(currentOs); this.hasCurrentOsDesktopVersion = hasDesktopVersion(currentOs);
} }
private async getDownloadUrlAsync(os: OperatingSystem): Promise<string> { private async getDownloadUrl(os: OperatingSystem): Promise<string> {
const context = await ApplicationFactory.Current.getAppAsync(); const context = await ApplicationFactory.Current.getApp();
return context.info.getDownloadUrl(os); return context.info.getDownloadUrl(os);
} }
} }

View File

@@ -43,7 +43,7 @@ export default class PrivacyPolicy extends Vue {
public isDesktop = Environment.CurrentEnvironment.isDesktop; public isDesktop = Environment.CurrentEnvironment.isDesktop;
public async created() { public async created() {
const app = await ApplicationFactory.Current.getAppAsync(); const app = await ApplicationFactory.Current.getApp();
this.initialize(app); this.initialize(app);
} }

View File

@@ -65,7 +65,7 @@ export default class TheFooter extends Vue {
public homepageUrl: string = ''; public homepageUrl: string = '';
public async created() { public async created() {
const app = await ApplicationFactory.Current.getAppAsync(); const app = await ApplicationFactory.Current.getApp();
this.initialize(app); this.initialize(app);
} }

View File

@@ -15,7 +15,7 @@ export default class TheHeader extends Vue {
public subtitle = ''; public subtitle = '';
public async created() { public async created() {
const app = await ApplicationFactory.Current.getAppAsync(); const app = await ApplicationFactory.Current.getApp();
this.title = app.info.name; this.title = app.info.name;
} }
} }

View File

@@ -26,8 +26,8 @@ export default class TheSearchBar extends StatefulVue {
public searchQuery = ''; public searchQuery = '';
@Watch('searchQuery') @Watch('searchQuery')
public async updateFilterAsync(newFilter: |string) { public async updateFilter(newFilter: |string) {
const context = await this.getCurrentContextAsync(); const context = await this.getCurrentContext();
const filter = context.state.filter; const filter = context.state.filter;
if (!newFilter) { if (!newFilter) {
filter.removeFilter(); filter.removeFilter();

View File

@@ -4,8 +4,8 @@ import { ProgressInfo } from 'electron-builder';
import { UpdateProgressBar } from './UpdateProgressBar'; import { UpdateProgressBar } from './UpdateProgressBar';
import log from 'electron-log'; import log from 'electron-log';
export async function handleAutoUpdateAsync() { export async function handleAutoUpdate() {
if (await askDownloadAndInstallAsync() === DownloadDialogResult.NotNow) { if (await askDownloadAndInstall() === DownloadDialogResult.NotNow) {
return; return;
} }
startHandlingUpdateProgress(); startHandlingUpdateProgress();
@@ -29,12 +29,12 @@ function startHandlingUpdateProgress() {
autoUpdater.on('update-downloaded', async (info: UpdateInfo) => { autoUpdater.on('update-downloaded', async (info: UpdateInfo) => {
log.info('@update-downloaded@\n', info); log.info('@update-downloaded@\n', info);
progressBar.close(); progressBar.close();
await handleUpdateDownloadedAsync(); await handleUpdateDownloaded();
}); });
} }
async function handleUpdateDownloadedAsync() { async function handleUpdateDownloaded() {
if (await askRestartAndInstallAsync() === InstallDialogResult.NotNow) { if (await askRestartAndInstall() === InstallDialogResult.NotNow) {
return; return;
} }
setTimeout(() => autoUpdater.quitAndInstall(), 1); setTimeout(() => autoUpdater.quitAndInstall(), 1);
@@ -44,7 +44,7 @@ enum DownloadDialogResult {
Install = 0, Install = 0,
NotNow = 1, NotNow = 1,
} }
async function askDownloadAndInstallAsync(): Promise<DownloadDialogResult> { async function askDownloadAndInstall(): Promise<DownloadDialogResult> {
const updateDialogResult = await dialog.showMessageBox({ const updateDialogResult = await dialog.showMessageBox({
type: 'question', type: 'question',
buttons: ['Install', 'Not now' ], buttons: ['Install', 'Not now' ],
@@ -61,7 +61,7 @@ enum InstallDialogResult {
InstallAndRestart = 0, InstallAndRestart = 0,
NotNow = 1, NotNow = 1,
} }
async function askRestartAndInstallAsync(): Promise<InstallDialogResult> { async function askRestartAndInstall(): Promise<InstallDialogResult> {
const installDialogResult = await dialog.showMessageBox({ const installDialogResult = await dialog.showMessageBox({
type: 'question', type: 'question',
buttons: ['Install and restart', 'Later'], buttons: ['Install and restart', 'Later'],

View File

@@ -12,8 +12,8 @@ export function requiresManualUpdate(): boolean {
return process.platform === 'darwin'; return process.platform === 'darwin';
} }
export async function handleManualUpdateAsync(info: UpdateInfo) { export async function handleManualUpdate(info: UpdateInfo) {
const result = await askForVisitingWebsiteForManualUpdateAsync(); const result = await askForVisitingWebsiteForManualUpdate();
if (result === ManualDownloadDialogResult.NoAction) { if (result === ManualDownloadDialogResult.NoAction) {
return; return;
} }
@@ -26,7 +26,7 @@ export async function handleManualUpdateAsync(info: UpdateInfo) {
if (result === ManualDownloadDialogResult.VisitReleasesPage) { if (result === ManualDownloadDialogResult.VisitReleasesPage) {
await shell.openExternal(project.releaseUrl); await shell.openExternal(project.releaseUrl);
} else if (result === ManualDownloadDialogResult.UpdateNow) { } else if (result === ManualDownloadDialogResult.UpdateNow) {
await downloadAsync(info, project); await download(info, project);
} }
} }
@@ -35,7 +35,7 @@ enum ManualDownloadDialogResult {
UpdateNow = 1, UpdateNow = 1,
VisitReleasesPage = 2, VisitReleasesPage = 2,
} }
async function askForVisitingWebsiteForManualUpdateAsync(): Promise<ManualDownloadDialogResult> { async function askForVisitingWebsiteForManualUpdate(): Promise<ManualDownloadDialogResult> {
const visitPageResult = await dialog.showMessageBox({ const visitPageResult = await dialog.showMessageBox({
type: 'info', type: 'info',
buttons: [ buttons: [
@@ -54,7 +54,7 @@ async function askForVisitingWebsiteForManualUpdateAsync(): Promise<ManualDownlo
return visitPageResult.response; return visitPageResult.response;
} }
async function downloadAsync(info: UpdateInfo, project: ProjectInformation) { async function download(info: UpdateInfo, project: ProjectInformation) {
log.info('Downloading update manually'); log.info('Downloading update manually');
const progressBar = new UpdateProgressBar(); const progressBar = new UpdateProgressBar();
progressBar.showIndeterminateState(); progressBar.showIndeterminateState();
@@ -69,7 +69,7 @@ async function downloadAsync(info: UpdateInfo, project: ProjectInformation) {
await fs.promises.mkdir(parentFolder, { recursive: true }); await fs.promises.mkdir(parentFolder, { recursive: true });
} }
const dmgFileUrl = project.getDownloadUrl(OperatingSystem.macOS); const dmgFileUrl = project.getDownloadUrl(OperatingSystem.macOS);
await downloadFileWithProgressAsync(dmgFileUrl, filePath, await downloadFileWithProgress(dmgFileUrl, filePath,
(percentage) => { progressBar.showPercentage(percentage); }); (percentage) => { progressBar.showPercentage(percentage); });
await shell.openPath(filePath); await shell.openPath(filePath);
progressBar.close(); progressBar.close();
@@ -81,7 +81,7 @@ async function downloadAsync(info: UpdateInfo, project: ProjectInformation) {
type ProgressCallback = (progress: number) => void; type ProgressCallback = (progress: number) => void;
async function downloadFileWithProgressAsync( async function downloadFileWithProgress(
url: string, filePath: string, progressHandler: ProgressCallback) { url: string, filePath: string, progressHandler: ProgressCallback) {
// We don't download through autoUpdater as it cannot download DMG but requires distributing ZIP // We don't download through autoUpdater as it cannot download DMG but requires distributing ZIP
log.info(`Fetching ${url}`); log.info(`Fetching ${url}`);
@@ -100,10 +100,10 @@ async function downloadFileWithProgressAsync(
if (!reader) { if (!reader) {
throw new Error('No response body'); throw new Error('No response body');
} }
await streamWithProgressAsync(contentLength, reader, writer, progressHandler); await streamWithProgress(contentLength, reader, writer, progressHandler);
} }
async function streamWithProgressAsync( async function streamWithProgress(
totalLength: number, totalLength: number,
readStream: NodeJS.ReadableStream, readStream: NodeJS.ReadableStream,
writeStream: fs.WriteStream, writeStream: fs.WriteStream,

View File

@@ -1,10 +1,10 @@
import { autoUpdater, UpdateInfo } from 'electron-updater'; import { autoUpdater, UpdateInfo } from 'electron-updater';
import log from 'electron-log'; import log from 'electron-log';
import { handleManualUpdateAsync, requiresManualUpdate } from './ManualUpdater'; import { handleManualUpdate, requiresManualUpdate } from './ManualUpdater';
import { handleAutoUpdateAsync } from './AutoUpdater'; import { handleAutoUpdate } from './AutoUpdater';
interface IUpdater { interface IUpdater {
checkForUpdatesAsync(): Promise<void>; checkForUpdates(): Promise<void>;
} }
export function setupAutoUpdater(): IUpdater { export function setupAutoUpdater(): IUpdater {
@@ -21,20 +21,20 @@ export function setupAutoUpdater(): IUpdater {
return; return;
} }
isAlreadyHandled = true; isAlreadyHandled = true;
await handleAvailableUpdateAsync(info); await handleAvailableUpdate(info);
}); });
return { return {
checkForUpdatesAsync: async () => { checkForUpdates: async () => {
// autoUpdater.emit('update-available'); // For testing // autoUpdater.emit('update-available'); // For testing
await autoUpdater.checkForUpdates(); await autoUpdater.checkForUpdates();
}, },
}; };
} }
async function handleAvailableUpdateAsync(info: UpdateInfo) { async function handleAvailableUpdate(info: UpdateInfo) {
if (requiresManualUpdate()) { if (requiresManualUpdate()) {
await handleManualUpdateAsync(info); await handleManualUpdate(info);
return; return;
} }
await handleAutoUpdateAsync(); await handleAutoUpdate();
} }

View File

@@ -124,7 +124,7 @@ function loadApplication(window: BrowserWindow) {
// Load the index.html when not in development // Load the index.html when not in development
loadUrlWithNodeWorkaround(win, 'app://./index.html'); loadUrlWithNodeWorkaround(win, 'app://./index.html');
const updater = setupAutoUpdater(); const updater = setupAutoUpdater();
updater.checkForUpdatesAsync(); updater.checkForUpdates();
} }
} }

View File

@@ -3,7 +3,7 @@ import { expect } from 'chai';
import { parseApplication } from '@/application/Parser/ApplicationParser'; import { parseApplication } from '@/application/Parser/ApplicationParser';
import { IApplication } from '@/domain/IApplication'; import { IApplication } from '@/domain/IApplication';
import { IUrlStatus } from './StatusChecker/IUrlStatus'; import { IUrlStatus } from './StatusChecker/IUrlStatus';
import { getUrlStatusesInParallelAsync, IBatchRequestOptions } from './StatusChecker/BatchStatusChecker'; import { getUrlStatusesInParallel, IBatchRequestOptions } from './StatusChecker/BatchStatusChecker';
describe('collections', () => { describe('collections', () => {
// arrange // arrange
@@ -25,7 +25,7 @@ describe('collections', () => {
const testTimeoutInMs = urls.length * 60000 /* 1 minute */; const testTimeoutInMs = urls.length * 60000 /* 1 minute */;
it('have no dead urls', async () => { it('have no dead urls', async () => {
// act // act
const results = await getUrlStatusesInParallelAsync(urls, options); const results = await getUrlStatusesInParallel(urls, options);
// assert // assert
const deadUrls = results.filter((r) => r.code !== 200); const deadUrls = results.filter((r) => r.code !== 200);
expect(deadUrls).to.have.lengthOf(0, printUrls(deadUrls)); expect(deadUrls).to.have.lengthOf(0, printUrls(deadUrls));

View File

@@ -1,16 +1,16 @@
import { sleepAsync } from '@/infrastructure/Threading/AsyncSleep'; import { sleep } from '@/infrastructure/Threading/AsyncSleep';
import { IUrlStatus } from './IUrlStatus'; import { IUrlStatus } from './IUrlStatus';
import { getUrlStatusAsync, IRequestOptions } from './Requestor'; import { getUrlStatus, IRequestOptions } from './Requestor';
import { groupUrlsByDomain } from './UrlPerDomainGrouper'; import { groupUrlsByDomain } from './UrlPerDomainGrouper';
export async function getUrlStatusesInParallelAsync( export async function getUrlStatusesInParallel(
urls: string[], urls: string[],
options?: IBatchRequestOptions): Promise<IUrlStatus[]> { options?: IBatchRequestOptions): Promise<IUrlStatus[]> {
// urls = [ 'https://privacy.sexy' ]; // Here to comment out when testing // urls = [ 'https://privacy.sexy' ]; // Here to comment out when testing
const uniqueUrls = Array.from(new Set(urls)); const uniqueUrls = Array.from(new Set(urls));
options = { ...DefaultOptions, ...options }; options = { ...DefaultOptions, ...options };
console.log('Options: ', options); // tslint:disable-line: no-console console.log('Options: ', options); // tslint:disable-line: no-console
const results = await requestAsync(uniqueUrls, options); const results = await request(uniqueUrls, options);
return results; return results;
} }
@@ -35,19 +35,19 @@ const DefaultOptions: IBatchRequestOptions = {
}, },
}; };
function requestAsync(urls: string[], options: IBatchRequestOptions): Promise<IUrlStatus[]> { function request(urls: string[], options: IBatchRequestOptions): Promise<IUrlStatus[]> {
if (!options.domainOptions.sameDomainParallelize) { if (!options.domainOptions.sameDomainParallelize) {
return runOnEachDomainWithDelayAsync( return runOnEachDomainWithDelay(
urls, urls,
(url) => getUrlStatusAsync(url, options.requestOptions), (url) => getUrlStatus(url, options.requestOptions),
options.domainOptions.sameDomainDelayInMs); options.domainOptions.sameDomainDelayInMs);
} else { } else {
return Promise.all( return Promise.all(
urls.map((url) => getUrlStatusAsync(url, options.requestOptions))); urls.map((url) => getUrlStatus(url, options.requestOptions)));
} }
} }
async function runOnEachDomainWithDelayAsync( async function runOnEachDomainWithDelay(
urls: string[], urls: string[],
action: (url: string) => Promise<IUrlStatus>, action: (url: string) => Promise<IUrlStatus>,
delayInMs: number): Promise<IUrlStatus[]> { delayInMs: number): Promise<IUrlStatus[]> {
@@ -58,7 +58,7 @@ async function runOnEachDomainWithDelayAsync(
const status = await action(url); const status = await action(url);
results.push(status); results.push(status);
if (results.length !== group.length) { if (results.length !== group.length) {
await sleepAsync(delayInMs); await sleep(delayInMs);
} }
} }
return results; return results;

View File

@@ -1,9 +1,9 @@
import { sleepAsync } from '@/infrastructure/Threading/AsyncSleep'; import { sleep } from '@/infrastructure/Threading/AsyncSleep';
import { IUrlStatus } from './IUrlStatus'; import { IUrlStatus } from './IUrlStatus';
const DefaultBaseRetryIntervalInMs = 5 /* sec */ * 1000; const DefaultBaseRetryIntervalInMs = 5 /* sec */ * 1000;
export async function retryWithExponentialBackOffAsync( export async function retryWithExponentialBackOff(
action: () => Promise<IUrlStatus>, action: () => Promise<IUrlStatus>,
baseRetryIntervalInMs: number = DefaultBaseRetryIntervalInMs, baseRetryIntervalInMs: number = DefaultBaseRetryIntervalInMs,
currentRetry = 1): Promise<IUrlStatus> { currentRetry = 1): Promise<IUrlStatus> {
@@ -14,8 +14,8 @@ export async function retryWithExponentialBackOffAsync(
const exponentialBackOffInMs = getRetryTimeoutInMs(currentRetry, baseRetryIntervalInMs); const exponentialBackOffInMs = getRetryTimeoutInMs(currentRetry, baseRetryIntervalInMs);
// tslint:disable-next-line: no-console // tslint:disable-next-line: no-console
console.log(`Retrying (${currentRetry}) in ${exponentialBackOffInMs / 1000} seconds`, status); console.log(`Retrying (${currentRetry}) in ${exponentialBackOffInMs / 1000} seconds`, status);
await sleepAsync(exponentialBackOffInMs); await sleep(exponentialBackOffInMs);
return retryWithExponentialBackOffAsync(action, baseRetryIntervalInMs, currentRetry + 1); return retryWithExponentialBackOff(action, baseRetryIntervalInMs, currentRetry + 1);
} }
} }
return status; return status;

View File

@@ -21,11 +21,11 @@ Coming soon 🚧
Programmatic usage is supported both on Node.js and browser. Programmatic usage is supported both on Node.js and browser.
### `getUrlStatusesInParallelAsync` ### `getUrlStatusesInParallel`
```js ```js
// Simple example // Simple example
const statuses = await getUrlStatusesInParallelAsync([ 'https://privacy.sexy', /* ... */ ]); const statuses = await getUrlStatusesInParallel([ 'https://privacy.sexy', /* ... */ ]);
if(statuses.all((r) => r.code === 200)) { if(statuses.all((r) => r.code === 200)) {
console.log('All URLs are alive!'); console.log('All URLs are alive!');
} else { } else {
@@ -33,7 +33,7 @@ if(statuses.all((r) => r.code === 200)) {
} }
// Fastest configuration // Fastest configuration
const statuses = await getUrlStatusesInParallelAsync([ 'https://privacy.sexy', /* ... */ ], { const statuses = await getUrlStatusesInParallel([ 'https://privacy.sexy', /* ... */ ], {
domainOptions: { domainOptions: {
sameDomainParallelize: false, sameDomainParallelize: false,
} }
@@ -53,13 +53,13 @@ const statuses = await getUrlStatusesInParallelAsync([ 'https://privacy.sexy', /
- Sets delay between requests to same host (domain) if same domain parallelization is disabled. - Sets delay between requests to same host (domain) if same domain parallelization is disabled.
- `requestOptions` (*object*): See [request options](#request-options). - `requestOptions` (*object*): See [request options](#request-options).
### `getUrlStatusAsync` ### `getUrlStatus`
Checks whether single URL is dead or alive. Checks whether single URL is dead or alive.
```js ```js
// Simple example // Simple example
const status = await getUrlStatusAsync('https://privacy.sexy'); const status = await getUrlStatus('https://privacy.sexy');
console.log(`Status code: ${status.code}`); console.log(`Status code: ${status.code}`);
``` ```

View File

@@ -1,13 +1,13 @@
import { retryWithExponentialBackOffAsync } from './ExponentialBackOffRetryHandler'; import { retryWithExponentialBackOff } from './ExponentialBackOffRetryHandler';
import { IUrlStatus } from './IUrlStatus'; import { IUrlStatus } from './IUrlStatus';
import { fetchFollow, IFollowOptions } from './FetchFollow'; import { fetchFollow, IFollowOptions } from './FetchFollow';
export async function getUrlStatusAsync( export async function getUrlStatus(
url: string, url: string,
options: IRequestOptions = DefaultOptions): Promise<IUrlStatus> { options: IRequestOptions = DefaultOptions): Promise<IUrlStatus> {
options = { ...DefaultOptions, ...options }; options = { ...DefaultOptions, ...options };
const fetchOptions = getFetchOptions(url, options); const fetchOptions = getFetchOptions(url, options);
return retryWithExponentialBackOffAsync(async () => { return retryWithExponentialBackOff(async () => {
console.log('Requesting', url); // tslint:disable-line: no-console console.log('Requesting', url); // tslint:disable-line: no-console
let result: IUrlStatus; let result: IUrlStatus;
try { try {

View File

@@ -15,7 +15,7 @@ describe('ApplicationFactory', () => {
expect(act).to.throw(expectedError); expect(act).to.throw(expectedError);
}); });
}); });
describe('getAppAsync', () => { describe('getApp', () => {
it('returns result from the getter', async () => { it('returns result from the getter', async () => {
// arrange // arrange
const expected = new ApplicationStub(); const expected = new ApplicationStub();
@@ -23,10 +23,10 @@ describe('ApplicationFactory', () => {
const sut = new SystemUnderTest(getter); const sut = new SystemUnderTest(getter);
// act // act
const actual = await Promise.all( [ const actual = await Promise.all( [
sut.getAppAsync(), sut.getApp(),
sut.getAppAsync(), sut.getApp(),
sut.getAppAsync(), sut.getApp(),
sut.getAppAsync(), sut.getApp(),
]); ]);
// assert // assert
expect(actual.every((value) => value === expected)); expect(actual.every((value) => value === expected));
@@ -42,10 +42,10 @@ describe('ApplicationFactory', () => {
const sut = new SystemUnderTest(getter); const sut = new SystemUnderTest(getter);
// act // act
await Promise.all( [ await Promise.all( [
sut.getAppAsync(), sut.getApp(),
sut.getAppAsync(), sut.getApp(),
sut.getAppAsync(), sut.getApp(),
sut.getAppAsync(), sut.getApp(),
]); ]);
// assert // assert
expect(totalExecution).to.equal(1); expect(totalExecution).to.equal(1);

View File

@@ -2,7 +2,7 @@ import 'mocha';
import { expect } from 'chai'; import { expect } from 'chai';
import { OperatingSystem } from '@/domain/OperatingSystem'; import { OperatingSystem } from '@/domain/OperatingSystem';
import { ICategoryCollection } from '@/domain/ICategoryCollection'; import { ICategoryCollection } from '@/domain/ICategoryCollection';
import { buildContextAsync } from '@/application/Context/ApplicationContextFactory'; import { buildContext } from '@/application/Context/ApplicationContextFactory';
import { IApplicationFactory } from '@/application/IApplicationFactory'; import { IApplicationFactory } from '@/application/IApplicationFactory';
import { IApplication } from '@/domain/IApplication'; import { IApplication } from '@/domain/IApplication';
import { EnvironmentStub } from '@tests/unit/stubs/EnvironmentStub'; import { EnvironmentStub } from '@tests/unit/stubs/EnvironmentStub';
@@ -10,7 +10,7 @@ import { ApplicationStub } from '@tests/unit/stubs/ApplicationStub';
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub'; import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
describe('ApplicationContextFactory', () => { describe('ApplicationContextFactory', () => {
describe('buildContextAsync', () => { describe('buildContext', () => {
describe('factory', () => { describe('factory', () => {
it('sets application from factory', async () => { it('sets application from factory', async () => {
// arrange // arrange
@@ -18,7 +18,7 @@ describe('ApplicationContextFactory', () => {
new CategoryCollectionStub().withOs(OperatingSystem.macOS)); new CategoryCollectionStub().withOs(OperatingSystem.macOS));
const factoryMock = mockFactoryWithApp(expected); const factoryMock = mockFactoryWithApp(expected);
// act // act
const context = await buildContextAsync(factoryMock); const context = await buildContext(factoryMock);
// assert // assert
expect(expected).to.equal(context.app); expect(expected).to.equal(context.app);
}); });
@@ -32,7 +32,7 @@ describe('ApplicationContextFactory', () => {
const collection = new CategoryCollectionStub().withOs(expected); const collection = new CategoryCollectionStub().withOs(expected);
const factoryMock = mockFactoryWithCollection(collection); const factoryMock = mockFactoryWithCollection(collection);
// act // act
const context = await buildContextAsync(factoryMock, environment); const context = await buildContext(factoryMock, environment);
// assert // assert
const actual = context.state.os; const actual = context.state.os;
expect(expected).to.equal(actual); expect(expected).to.equal(actual);
@@ -45,7 +45,7 @@ describe('ApplicationContextFactory', () => {
const collection = new CategoryCollectionStub().withOs(expected); const collection = new CategoryCollectionStub().withOs(expected);
const factoryMock = mockFactoryWithCollection(collection); const factoryMock = mockFactoryWithCollection(collection);
// act // act
const context = await buildContextAsync(factoryMock, environment); const context = await buildContext(factoryMock, environment);
// assert // assert
const actual = context.state.os; const actual = context.state.os;
expect(expected).to.equal(actual); expect(expected).to.equal(actual);
@@ -62,7 +62,7 @@ describe('ApplicationContextFactory', () => {
const app = new ApplicationStub().withCollections(...allCollections); const app = new ApplicationStub().withCollections(...allCollections);
const factoryMock = mockFactoryWithApp(app); const factoryMock = mockFactoryWithApp(app);
// act // act
const context = await buildContextAsync(factoryMock, environment); const context = await buildContext(factoryMock, environment);
// assert // assert
const actual = context.state.os; const actual = context.state.os;
expect(expectedOs).to.equal(actual, `Expected: ${OperatingSystem[expectedOs]}, actual: ${OperatingSystem[actual]}`); expect(expectedOs).to.equal(actual, `Expected: ${OperatingSystem[expectedOs]}, actual: ${OperatingSystem[actual]}`);
@@ -78,6 +78,6 @@ function mockFactoryWithCollection(result: ICategoryCollection): IApplicationFac
function mockFactoryWithApp(app: IApplication): IApplicationFactory { function mockFactoryWithApp(app: IApplication): IApplicationFactory {
return { return {
getAppAsync: () => Promise.resolve(app), getApp: () => Promise.resolve(app),
}; };
} }

View File

@@ -5,7 +5,7 @@ import { OperatingSystem } from '@/domain/OperatingSystem';
import { CodeRunner } from '@/infrastructure/CodeRunner'; import { CodeRunner } from '@/infrastructure/CodeRunner';
describe('CodeRunner', () => { describe('CodeRunner', () => {
describe('runCodeAsync', () => { describe('runCode', () => {
it('creates temporary directory recursively', async () => { it('creates temporary directory recursively', async () => {
// arrange // arrange
const expectedDir = 'expected-dir'; const expectedDir = 'expected-dir';
@@ -17,7 +17,7 @@ describe('CodeRunner', () => {
// act // act
await context await context
.withFolderName(folderName) .withFolderName(folderName)
.runCodeAsync(); .runCode();
// assert // assert
expect(context.mocks.fs.mkdirHistory.length).to.equal(1); expect(context.mocks.fs.mkdirHistory.length).to.equal(1);
@@ -42,7 +42,7 @@ describe('CodeRunner', () => {
.withCode(expectedCode) .withCode(expectedCode)
.withFolderName(folderName) .withFolderName(folderName)
.withExtension(extension) .withExtension(extension)
.runCodeAsync(); .runCode();
// assert // assert
expect(context.mocks.fs.writeFileHistory.length).to.equal(1); expect(context.mocks.fs.writeFileHistory.length).to.equal(1);
@@ -66,7 +66,7 @@ describe('CodeRunner', () => {
await context await context
.withFolderName(folderName) .withFolderName(folderName)
.withExtension(extension) .withExtension(extension)
.runCodeAsync(); .runCode();
// assert // assert
expect(context.mocks.fs.chmodCallHistory.length).to.equal(1); expect(context.mocks.fs.chmodCallHistory.length).to.equal(1);
@@ -93,7 +93,7 @@ describe('CodeRunner', () => {
// act // act
await context await context
.withOs(data.os) .withOs(data.os)
.runCodeAsync(); .runCode();
// assert // assert
expect(context.mocks.child_process.executionHistory.length).to.equal(1); expect(context.mocks.child_process.executionHistory.length).to.equal(1);
@@ -109,7 +109,7 @@ describe('CodeRunner', () => {
context.mocks.path.setupJoinSequence('non-important-folder-name1', 'non-important-folder-name2'); context.mocks.path.setupJoinSequence('non-important-folder-name1', 'non-important-folder-name2');
// act // act
await context.runCodeAsync(); await context.runCode();
// assert // assert
const actualOrder = context.mocks.commandHistory.filter((command) => expectedOrder.includes(command)); const actualOrder = context.mocks.commandHistory.filter((command) => expectedOrder.includes(command));
@@ -126,9 +126,9 @@ class TestContext {
private fileExtension: string = 'fileExtension'; private fileExtension: string = 'fileExtension';
private env = mockEnvironment(OperatingSystem.Windows); private env = mockEnvironment(OperatingSystem.Windows);
public async runCodeAsync(): Promise<void> { public async runCode(): Promise<void> {
const runner = new CodeRunner(this.mocks, this.env); const runner = new CodeRunner(this.mocks, this.env);
await runner.runCodeAsync(this.code, this.folderName, this.fileExtension); await runner.runCode(this.code, this.folderName, this.fileExtension);
} }
public withOs(os: OperatingSystem) { public withOs(os: OperatingSystem) {
this.env = mockEnvironment(os); this.env = mockEnvironment(os);

View File

@@ -1,7 +1,7 @@
import 'mocha'; import 'mocha';
import { expect } from 'chai'; import { expect } from 'chai';
import { AsyncLazy } from '@/infrastructure/Threading/AsyncLazy'; import { AsyncLazy } from '@/infrastructure/Threading/AsyncLazy';
import { sleepAsync } from '@/infrastructure/Threading/AsyncSleep'; import { sleep } from '@/infrastructure/Threading/AsyncSleep';
describe('AsyncLazy', () => { describe('AsyncLazy', () => {
it('returns value from lambda', async () => { it('returns value from lambda', async () => {
@@ -10,7 +10,7 @@ describe('AsyncLazy', () => {
const lambda = () => Promise.resolve(expected); const lambda = () => Promise.resolve(expected);
const sut = new AsyncLazy(lambda); const sut = new AsyncLazy(lambda);
// act // act
const actual = await sut.getValueAsync(); const actual = await sut.getValue();
// assert // assert
expect(actual).to.equal(expected); expect(actual).to.equal(expected);
}); });
@@ -26,7 +26,7 @@ describe('AsyncLazy', () => {
}); });
const results = new Array<number>(); const results = new Array<number>();
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
results.push(await sut.getValueAsync()); results.push(await sut.getValue());
} }
// assert // assert
expect(totalExecuted).to.equal(1); expect(totalExecuted).to.equal(1);
@@ -35,16 +35,16 @@ describe('AsyncLazy', () => {
it('when running long-running task in parallel', async () => { it('when running long-running task in parallel', async () => {
// act // act
const sut = new AsyncLazy(async () => { const sut = new AsyncLazy(async () => {
await sleepAsync(100); await sleep(100);
totalExecuted++; totalExecuted++;
return Promise.resolve(totalExecuted); return Promise.resolve(totalExecuted);
}); });
const results = await Promise.all([ const results = await Promise.all([
sut.getValueAsync(), sut.getValue(),
sut.getValueAsync(), sut.getValue(),
sut.getValueAsync(), sut.getValue(),
sut.getValueAsync(), sut.getValue(),
sut.getValueAsync()]); sut.getValue()]);
// assert // assert
expect(totalExecuted).to.equal(1); expect(totalExecuted).to.equal(1);
expect(results).to.deep.equal([1, 1, 1, 1, 1]); expect(results).to.deep.equal([1, 1, 1, 1, 1]);

View File

@@ -1,33 +1,35 @@
import 'mocha'; import 'mocha';
import { expect } from 'chai'; import { expect } from 'chai';
import { sleepAsync, SchedulerType } from '@/infrastructure/Threading/AsyncSleep'; import { sleep, SchedulerType } from '@/infrastructure/Threading/AsyncSleep';
describe('AsyncSleep', () => { describe('AsyncSleep', () => {
it('fulfills after delay', async () => { describe('sleep', () => {
// arrange it('fulfills after delay', async () => {
const delayInMs = 10; // arrange
const scheduler = new SchedulerMock(); const delayInMs = 10;
// act const scheduler = new SchedulerMock();
const sleep = sleepAsync(delayInMs, scheduler.mock); // act
const promiseState = watchPromiseState(sleep); const promise = sleep(delayInMs, scheduler.mock);
scheduler.tickNext(delayInMs); const promiseState = watchPromiseState(promise);
await flushPromiseResolutionQueue(); scheduler.tickNext(delayInMs);
// assert await flushPromiseResolutionQueue();
const actual = promiseState.isFulfilled(); // assert
expect(actual).to.equal(true); const actual = promiseState.isFulfilled();
}); expect(actual).to.equal(true);
it('pending before delay', async () => { });
// arrange it('pending before delay', async () => {
const delayInMs = 10; // arrange
const scheduler = new SchedulerMock(); const delayInMs = 10;
// act const scheduler = new SchedulerMock();
const sleep = sleepAsync(delayInMs, scheduler.mock); // act
const promiseState = watchPromiseState(sleep); const promise = sleep(delayInMs, scheduler.mock);
scheduler.tickNext(delayInMs / 5); const promiseState = watchPromiseState(promise);
await flushPromiseResolutionQueue(); scheduler.tickNext(delayInMs / 5);
// assert await flushPromiseResolutionQueue();
const actual = promiseState.isPending(); // assert
expect(actual).to.equal(true); const actual = promiseState.isPending();
expect(actual).to.equal(true);
});
}); });
}); });