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:
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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}`);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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'],
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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}`);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user