From dd0a4fe7ca5808e231d93e66284af6be8957b02b Mon Sep 17 00:00:00 2001 From: Jon Edvald Date: Sat, 21 Apr 2018 14:47:54 +0200 Subject: [PATCH] feat: add watch flag to test and build commands --- src/commands/build.ts | 16 ++---- src/commands/deploy.ts | 34 ++++-------- src/commands/test.ts | 36 +++---------- src/plugin-context.ts | 72 ++++++++++++++++++++++++++ src/plugins/generic.ts | 4 +- src/task-graph.ts | 38 ++++++++++++-- src/tasks/build.ts | 4 ++ src/tasks/deploy.ts | 4 ++ src/tasks/push.ts | 4 ++ src/tasks/test.ts | 9 +++- src/types/task.ts | 2 + test/helpers.ts | 6 +++ test/src/commands/build.ts | 13 +++-- test/src/commands/deploy.ts | 5 +- test/src/commands/push.ts | 13 ++--- test/src/commands/test.ts | 13 +++-- test/src/task-graph.ts | 100 ++++++++++++++++++++++++++---------- 17 files changed, 256 insertions(+), 117 deletions(-) diff --git a/src/commands/build.ts b/src/commands/build.ts index 9b664efff9..8a5d10ad63 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -21,6 +21,7 @@ export const buildArguments = { export const buildOptions = { force: new BooleanParameter({ help: "Force rebuild of module(s)" }), + watch: new BooleanParameter({ help: "Watch for changes in module(s) and auto-build", alias: "w" }), } export type BuildArguments = ParameterValues @@ -36,19 +37,12 @@ export class BuildCommand extends Command { await ctx.clearBuilds() const names = args.module ? args.module.split(",") : undefined - const modules = await ctx.getModules(names) - - for (const module of values(modules)) { - await ctx.addTask(new BuildTask(ctx, module, opts.force)) - } + const modules = values(await ctx.getModules(names)) ctx.log.header({ emoji: "hammer", command: "build" }) - const result = await ctx.processTasks() - - ctx.log.info("") - ctx.log.info({ emoji: "heavy_check_mark", msg: chalk.green("Done!\n") }) - - return result + return await ctx.processModules(modules, opts.watch, async (module) => { + await ctx.addTask(new BuildTask(ctx, module, opts.force)) + }) } } diff --git a/src/commands/deploy.ts b/src/commands/deploy.ts index 851462f4f0..bb8967f7ff 100644 --- a/src/commands/deploy.ts +++ b/src/commands/deploy.ts @@ -8,9 +8,7 @@ import { PluginContext } from "../plugin-context" import { DeployTask } from "../tasks/deploy" -import { watchModules } from "../watch" import { BooleanParameter, Command, ParameterValues, StringParameter } from "./base" -import chalk from "chalk" import { TaskResults } from "../task-graph" import { values } from "lodash" @@ -22,9 +20,9 @@ export const deployArgs = { } export const deployOpts = { - watch: new BooleanParameter({ help: "Listen for changes in module(s) and auto-deploy", alias: "w" }), force: new BooleanParameter({ help: "Force redeploy of service(s)" }), "force-build": new BooleanParameter({ help: "Force rebuild of module(s)" }), + watch: new BooleanParameter({ help: "Watch for changes in module(s) and auto-deploy", alias: "w" }), } export type Args = ParameterValues @@ -37,7 +35,7 @@ export class DeployCommand extends Command arguments = deployArgs options = deployOpts - async action(ctx: PluginContext, args: Args, opts: Opts): Promise { + async action(ctx: PluginContext, args: Args, opts: Opts): Promise { const names = args.service ? args.service.split(",") : undefined const services = await ctx.getServices(names) @@ -53,29 +51,15 @@ export class DeployCommand extends Command const force = opts.force const forceBuild = opts["force-build"] - for (const service of values(services)) { - const task = new DeployTask(ctx, service, force, forceBuild) - await ctx.addTask(task) - } - ctx.log.header({ emoji: "rocket", command: "Deploy" }) - if (watch) { - const modules = Array.from(new Set(values(services).map(s => s.module))) - - await watchModules(ctx, modules, async (_, module) => { - const servicesToDeploy = values(await module.getServices()).filter(s => !!services[s.name]) - for (const service of servicesToDeploy) { - await ctx.addTask(new DeployTask(ctx, service, true, false)) - } - }) - } else { - const result = await ctx.processTasks() + const modules = Array.from(new Set(values(services).map(s => s.module))) - ctx.log.info("") - ctx.log.info({ emoji: "heavy_check_mark", msg: chalk.green("Done!\n") }) - - return result - } + return ctx.processModules(modules, watch, async (module) => { + const servicesToDeploy = values(await module.getServices()).filter(s => !!services[s.name]) + for (const service of servicesToDeploy) { + await ctx.addTask(new DeployTask(ctx, service, force, forceBuild)) + } + }) } } diff --git a/src/commands/test.ts b/src/commands/test.ts index 0e84253ac0..f23cf7775e 100644 --- a/src/commands/test.ts +++ b/src/commands/test.ts @@ -8,9 +8,8 @@ import { PluginContext } from "../plugin-context" import { BooleanParameter, Command, ParameterValues, StringParameter } from "./base" -import { values, padEnd } from "lodash" +import { values } from "lodash" import { TestTask } from "../tasks/test" -import { splitFirst } from "../util" import chalk from "chalk" import { TaskResults } from "../task-graph" @@ -28,6 +27,7 @@ export const testOpts = { }), force: new BooleanParameter({ help: "Force re-test of module(s)", alias: "f" }), "force-build": new BooleanParameter({ help: "Force rebuild of module(s)" }), + watch: new BooleanParameter({ help: "Watch for changes in module(s) and auto-test", alias: "w" }), } export type Args = ParameterValues @@ -42,7 +42,7 @@ export class TestCommand extends Command { async action(ctx: PluginContext, args: Args, opts: Opts): Promise { const names = args.module ? args.module.split(",") : undefined - const modules = await ctx.getModules(names) + const modules = values(await ctx.getModules(names)) ctx.log.header({ emoji: "thermometer", @@ -51,7 +51,7 @@ export class TestCommand extends Command { await ctx.configureEnvironment() - for (const module of values(modules)) { + const results = await ctx.processModules(modules, opts.watch, async (module) => { const config = await module.getConfig() for (const testName of Object.keys(config.test)) { @@ -62,33 +62,9 @@ export class TestCommand extends Command { const task = new TestTask(ctx, module, testName, testSpec, opts.force, opts["force-build"]) await ctx.addTask(task) } - } - - const results = await ctx.processTasks() - let failed = 0 - - for (const key in results) { - // TODO: this is brittle, we should have a more verbose data structure coming out of the TaskGraph - const [type, taskKey] = splitFirst(key, ".") - - if (type !== "test") { - continue - } - - const result = results[key] - - if (!result.success) { - const [moduleName, testType] = splitFirst(taskKey, ".") - const divider = padEnd("—", 80) - - ctx.log.error(`${testType} tests for ${moduleName} failed. Here is the output:`) - ctx.log.error(divider) - ctx.log.error(result.output) - ctx.log.error(divider + "\n") + }) - failed++ - } - } + const failed = values(results).filter(r => !!r.error).length if (failed) { ctx.log.error({ emoji: "warning", msg: `${failed} tests runs failed! See log output above.\n` }) diff --git a/src/plugin-context.ts b/src/plugin-context.ts index 498b3df12b..3321e796bd 100644 --- a/src/plugin-context.ts +++ b/src/plugin-context.ts @@ -16,6 +16,7 @@ import { LogEntry, } from "./logger" import { EntryStyle } from "./logger/types" +import { TaskResults } from "./task-graph" import { DeployTask } from "./tasks/deploy" import { PrimitiveMap, @@ -50,8 +51,17 @@ import { mapValues, toPairs, values, + padEnd, } from "lodash" +import { + registerCleanupFunction, + sleep, +} from "./util" import { TreeVersion } from "./vcs/base" +import { + computeAutoReloadDependants, + FSWatcher, +} from "./watch" export type PluginContextGuard = { readonly [P in keyof (PluginActionParams | ModuleActionParams)]: (...args: any[]) => Promise @@ -114,6 +124,9 @@ export interface PluginContext extends PluginContextGuard, WrappedFromGarden { deployServices: ( params: { names?: string[], force?: boolean, forceBuild?: boolean, logEntry?: LogEntry }, ) => Promise + processModules: ( + modules: Module[], watch: boolean, process: (module: Module) => Promise, + ) => Promise } export function createPluginContext(garden: Garden): PluginContext { @@ -338,6 +351,65 @@ export function createPluginContext(garden: Garden): PluginContext { return ctx.processTasks() }, + + processModules: async (modules: Module[], watch: boolean, process: (module: Module) => Promise) => { + // TODO: log errors as they happen, instead of after processing all tasks + const logErrors = (taskResults: TaskResults) => { + for (const result of values(taskResults).filter(r => !!r.error)) { + const divider = padEnd("", 80, "—") + + ctx.log.error(`\nFailed ${result.description}. Here is the output:`) + ctx.log.error(divider) + ctx.log.error(result.error + "") + ctx.log.error(divider + "\n") + } + } + + for (const module of modules) { + await process(module) + } + + const results = await ctx.processTasks() + logErrors(results) + + if (!watch) { + return results + } + + const autoReloadDependants = await computeAutoReloadDependants(modules) + + async function handleChanges(module: Module) { + await process(module) + + const dependantsForModule = autoReloadDependants[module.name] + if (!dependantsForModule) { + return + } + + for (const dependant of dependantsForModule) { + await handleChanges(dependant) + } + } + + const watcher = new FSWatcher() + + // TODO: should the prefix here be different or set explicitly per run? + await watcher.watchModules(modules, "addTasksForAutoReload/", + async (changedModule) => { + ctx.log.info({ msg: `files changed for module ${changedModule.name}` }) + await handleChanges(changedModule) + logErrors(await ctx.processTasks()) + }) + + registerCleanupFunction("clearAutoReloadWatches", () => { + ctx.log.info({ msg: "Clearing autoreload watches" }) + watcher.end() + }) + + while (true) { + await sleep(1000) + } + }, } return ctx diff --git a/src/plugins/generic.ts b/src/plugins/generic.ts index f5e7cee2a0..18c5687058 100644 --- a/src/plugins/generic.ts +++ b/src/plugins/generic.ts @@ -67,13 +67,15 @@ export const genericPlugin = { } }, - async testModule({ module, testSpec }: TestModuleParams): Promise { + async testModule({ module, testName, testSpec }: TestModuleParams): Promise { const startedAt = new Date() const result = await spawn( testSpec.command[0], testSpec.command.slice(1), { cwd: module.path, ignoreError: true }, ) return { + moduleName: module.name, + testName, version: await module.getVersion(), success: result.code === 0, startedAt, diff --git a/src/task-graph.ts b/src/task-graph.ts index 11c06e3802..aa3d3da113 100644 --- a/src/task-graph.ts +++ b/src/task-graph.ts @@ -17,12 +17,20 @@ import { PluginContext } from "./plugin-context" class TaskGraphError extends Error { } +export interface TaskResult { + type: string + description: string + output?: any + dependencyResults?: TaskResults + error?: any +} + /* When multiple tasks with the same baseKey are completed during a call to processTasks, the result from the last processed is used (hence only one key-value pair here per baseKey). */ export interface TaskResults { - [baseKey: string]: any + [baseKey: string]: TaskResult } interface LogEntryMap { [key: string]: LogEntry } @@ -111,7 +119,7 @@ export class TaskGraph { async processTasksInternal(): Promise { const _this = this - let results: TaskResults = {} + const results: TaskResults = {} const loop = async () => { if (_this.index.length === 0) { @@ -129,7 +137,9 @@ export class TaskGraph { this.initLogging() return Bluebird.map(batch, async (node: TaskNode) => { + const type = node.getType() const baseKey = node.getBaseKey() + const description = node.getDescription() try { this.logTask(node) @@ -137,9 +147,14 @@ export class TaskGraph { const dependencyBaseKeys = (await node.task.getDependencies()) .map(task => task.getBaseKey()) + const dependencyResults = pick(results, dependencyBaseKeys) - results[baseKey] = await node.process(dependencyResults) + try { + results[baseKey] = await node.process(dependencyResults) + } catch (error) { + results[baseKey] = { type, description, error } + } } finally { this.completeTask(node) } @@ -346,6 +361,14 @@ class TaskNode { return getIndexKey(this.task) } + getDescription() { + return this.task.getDescription() + } + + getType() { + return this.task.type + } + // For testing/debugging purposes inspect(): object { return { @@ -356,7 +379,14 @@ class TaskNode { } async process(dependencyResults: TaskResults) { - return await this.task.process(dependencyResults) + const output = await this.task.process(dependencyResults) + + return { + type: this.getType(), + description: this.getDescription(), + output, + dependencyResults, + } } } diff --git a/src/tasks/build.ts b/src/tasks/build.ts index bc523b3a94..bd3ee0a9ee 100644 --- a/src/tasks/build.ts +++ b/src/tasks/build.ts @@ -30,6 +30,10 @@ export class BuildTask extends Task { return this.module.name } + getDescription() { + return `building ${this.module.name}` + } + async process(): Promise { if (!this.force && (await this.ctx.getModuleBuildStatus(this.module)).ready) { // this is necessary in case other modules depend on files from this one diff --git a/src/tasks/deploy.ts b/src/tasks/deploy.ts index a56e79479b..dba4c63963 100644 --- a/src/tasks/deploy.ts +++ b/src/tasks/deploy.ts @@ -45,6 +45,10 @@ export class DeployTask> extends Task { return this.service.name } + getDescription() { + return `deploying service ${this.service.name} (from module ${this.service.module.name})` + } + async process(): Promise { const entry = (this.logEntry || this.ctx.log).info({ section: this.service.name, diff --git a/src/tasks/push.ts b/src/tasks/push.ts index 205af465d7..637c5aad03 100644 --- a/src/tasks/push.ts +++ b/src/tasks/push.ts @@ -36,6 +36,10 @@ export class PushTask> extends Task { return this.module.name } + getDescription() { + return `pushing module ${this.module.name}` + } + async process(): Promise { if (!(await this.module.getConfig()).allowPush) { this.ctx.log.info({ diff --git a/src/tasks/test.ts b/src/tasks/test.ts index de8b7b4f36..a37db72d5c 100644 --- a/src/tasks/test.ts +++ b/src/tasks/test.ts @@ -15,6 +15,12 @@ import { Task } from "../types/task" import { EntryStyle } from "../logger/types" import chalk from "chalk" +class TestError extends Error { + toString() { + return this.message + } +} + export class TestTask extends Task { type = "test" @@ -79,7 +85,8 @@ export class TestTask extends Task { if (result.success) { entry.setSuccess({ msg: chalk.green(`Success`), append: true }) } else { - entry.error({ msg: chalk.red(`Failed!`), append: true }) + entry.setError({ msg: chalk.red(`Failed!`), append: true }) + throw new TestError(result.output) } return result diff --git a/src/types/task.ts b/src/types/task.ts index 228c83ba36..9e11d53258 100644 --- a/src/types/task.ts +++ b/src/types/task.ts @@ -36,6 +36,8 @@ export abstract class Task { return `${this.getBaseKey()}.${this.id}` } + abstract getDescription(): string + abstract async process(dependencyResults: TaskResults): Promise } diff --git a/test/helpers.ts b/test/helpers.ts index 216c7366e1..40be6ff65d 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -1,5 +1,6 @@ import * as td from "testdouble" import { resolve } from "path" +import { TaskResults } from "../src/task-graph" import { DeleteConfigParams, GetConfigParams, @@ -13,6 +14,7 @@ import { import { Garden } from "../src/garden" import { Module } from "../src/types/module" import { expect } from "chai" +import { mapValues } from "lodash" export const dataDir = resolve(__dirname, "data") @@ -156,3 +158,7 @@ export async function expectError(fn: Function, typeOrCallback: string | ((err: throw new Error(`Expected error (got no error)`) } } + +export function taskResultOutputs(results: TaskResults) { + return mapValues(results, r => r.output) +} diff --git a/test/src/commands/build.ts b/test/src/commands/build.ts index 0346e62533..351d61525c 100644 --- a/test/src/commands/build.ts +++ b/test/src/commands/build.ts @@ -1,15 +1,18 @@ import { BuildCommand } from "../../../src/commands/build" import { expect } from "chai" -import { makeTestContextA } from "../../helpers" +import { + makeTestContextA, + taskResultOutputs +} from "../../helpers" describe("commands.build", () => { it("should build all modules in a project", async () => { const ctx = await makeTestContextA() const command = new BuildCommand() - const result = await command.action(ctx, { module: undefined }, { force: true }) + const result = await command.action(ctx, { module: undefined }, { watch: false, force: true }) - expect(result).to.eql({ + expect(taskResultOutputs(result)).to.eql({ "build.module-a": { fresh: true, buildLog: "A\n" }, "build.module-b": { fresh: true, buildLog: "B\n" }, "build.module-c": {}, @@ -20,9 +23,9 @@ describe("commands.build", () => { const ctx = await makeTestContextA() const command = new BuildCommand() - const result = await command.action(ctx, { module: "module-b" }, { force: true }) + const result = await command.action(ctx, { module: "module-b" }, { watch: false, force: true }) - expect(result).to.eql({ + expect(taskResultOutputs(result)).to.eql({ "build.module-a": { fresh: true, buildLog: "A\n" }, "build.module-b": { fresh: true, buildLog: "B\n" }, }) diff --git a/test/src/commands/deploy.ts b/test/src/commands/deploy.ts index a0ee7908a4..d410e1005a 100644 --- a/test/src/commands/deploy.ts +++ b/test/src/commands/deploy.ts @@ -8,6 +8,7 @@ import { PluginFactory, } from "../../../src/types/plugin" import { ServiceState, ServiceStatus } from "../../../src/types/service" +import { taskResultOutputs } from "../../helpers" const testProvider: PluginFactory = () => { const testStatuses: { [key: string]: ServiceStatus } = { @@ -72,7 +73,7 @@ describe("commands.deploy", () => { }, ) - expect(result).to.eql({ + expect(taskResultOutputs(result)).to.eql({ "build.module-a": { fresh: true, buildLog: "A\n" }, "build.module-b": { fresh: true, buildLog: "B\n" }, "build.module-c": {}, @@ -99,7 +100,7 @@ describe("commands.deploy", () => { }, ) - expect(result).to.eql({ + expect(taskResultOutputs(result)).to.eql({ "build.module-a": { fresh: true, buildLog: "A\n" }, "build.module-b": { fresh: true, buildLog: "B\n" }, "deploy.service-a": { version: "1", state: "ready" }, diff --git a/test/src/commands/push.ts b/test/src/commands/push.ts index c3b1564f11..aa87c1462c 100644 --- a/test/src/commands/push.ts +++ b/test/src/commands/push.ts @@ -11,6 +11,7 @@ import { PushCommand } from "../../../src/commands/push" import { TreeVersion } from "../../../src/vcs/base" import { expectError, + taskResultOutputs, } from "../../helpers" const projectRootB = join(__dirname, "..", "..", "data", "test-project-b") @@ -103,7 +104,7 @@ describe("PushCommand", () => { }, ) - expect(result).to.eql({ + expect(taskResultOutputs(result)).to.eql({ "build.module-a": { fresh: false }, "build.module-b": { fresh: false }, "push.module-a": { pushed: true }, @@ -127,7 +128,7 @@ describe("PushCommand", () => { }, ) - expect(result).to.eql({ + expect(taskResultOutputs(result)).to.eql({ "build.module-a": { fresh: true }, "build.module-b": { fresh: true }, "push.module-a": { pushed: true }, @@ -151,7 +152,7 @@ describe("PushCommand", () => { }, ) - expect(result).to.eql({ + expect(taskResultOutputs(result)).to.eql({ "build.module-a": { fresh: false }, "push.module-a": { pushed: true }, }) @@ -172,7 +173,7 @@ describe("PushCommand", () => { }, ) - expect(result).to.eql({ + expect(taskResultOutputs(result)).to.eql({ "push.module-c": { pushed: false }, }) }) @@ -194,7 +195,7 @@ describe("PushCommand", () => { }, ) - expect(result).to.eql({ + expect(taskResultOutputs(result)).to.eql({ "build.module-a": { fresh: false }, "push.module-a": { pushed: false, message: chalk.yellow("No push handler available for module type generic") }, }) @@ -247,7 +248,7 @@ describe("PushCommand", () => { }, ) - expect(result).to.eql({ + expect(taskResultOutputs(result)).to.eql({ "build.module-a": { fresh: true }, "push.module-a": { pushed: true }, }) diff --git a/test/src/commands/test.ts b/test/src/commands/test.ts index 82275513fe..660b025ef7 100644 --- a/test/src/commands/test.ts +++ b/test/src/commands/test.ts @@ -1,5 +1,8 @@ import { expect } from "chai" -import { makeTestContextA } from "../../helpers" +import { + makeTestContextA, + taskResultOutputs +} from "../../helpers" import { TestCommand } from "../../../src/commands/test" import * as isSubset from "is-subset" @@ -11,10 +14,10 @@ describe("commands.test", () => { const result = await command.action( ctx, { module: undefined }, - { group: undefined, force: true, "force-build": true }, + { group: undefined, force: true, "force-build": true, watch: false }, ) - expect(isSubset(result, { + expect(isSubset(taskResultOutputs(result), { "build.module-a": { fresh: true, buildLog: "A\n", @@ -46,10 +49,10 @@ describe("commands.test", () => { const result = await command.action( ctx, { module: "module-a" }, - { group: undefined, force: true, "force-build": true }, + { group: undefined, force: true, "force-build": true, watch: false }, ) - expect(isSubset(result, { + expect(isSubset(taskResultOutputs(result), { "build.module-a": { fresh: true, buildLog: "A\n", diff --git a/test/src/task-graph.ts b/test/src/task-graph.ts index 51ed141053..598be9357b 100644 --- a/test/src/task-graph.ts +++ b/test/src/task-graph.ts @@ -2,7 +2,11 @@ import { join } from "path" import { expect } from "chai" import { Garden } from "../../src/garden" import { Task } from "../../src/types/task" -import { TaskGraph, TaskResults } from "../../src/task-graph" +import { + TaskGraph, + TaskResult, + TaskResults, +} from "../../src/task-graph" const projectRoot = join(__dirname, "..", "data", "test-project-empty") @@ -38,11 +42,15 @@ class TestTask extends Task { return this.id ? `${this.name}.${this.id}` : this.name } + getDescription() { + return this.getKey() + } + async process(dependencyResults: TaskResults) { const result = { result: "result-" + this.getKey(), dependencyResults } if (this.callback) { - await this.callback(this.getKey(), result) + await this.callback(this.getKey(), result.result) } return result @@ -65,9 +73,19 @@ describe("task-graph", () => { await graph.addTask(task) const results = await graph.processTasks() - expect(results).to.eql({ - a: { result: "result-a", dependencyResults: {} }, - }) + const expected: TaskResults = { + a: { + type: "test", + description: "a", + output: { + result: "result-a", + dependencyResults: {}, + }, + dependencyResults: {}, + }, + } + + expect(results).to.eql(expected) }) it("should process multiple tasks in dependency order", async () => { @@ -100,36 +118,64 @@ describe("task-graph", () => { const results = await graph.processTasks() - const resultA = { result: "result-a", dependencyResults: {} } - const resultB = { - result: "result-b", - dependencyResults: { a: resultA }, - } - const resultC = { - result: "result-c", - dependencyResults: { b: resultB }, + const resultA: TaskResult = { + type: "test", + description: "a", + output: { + result: "result-a", + dependencyResults: {}, + }, + dependencyResults: {}, } - - expect(results).to.eql(callbackResults) - expect(results).to.eql({ - a: resultA, - b: { + const resultB: TaskResult = { + type: "test", + description: "b", + output: { result: "result-b", dependencyResults: { a: resultA }, }, - c: { + dependencyResults: { a: resultA }, + } + const resultC: TaskResult = { + type: "test", + description: "c", + output: { result: "result-c", dependencyResults: { b: resultB }, }, + dependencyResults: { b: resultB }, + } + + const expected: TaskResults = { + a: resultA, + b: resultB, + c: resultC, d: { - result: "result-d", + type: "test", + description: "d", + output: { + result: "result-d", + dependencyResults: { + b: resultB, + c: resultC, + }, + }, dependencyResults: { b: resultB, c: resultC, }, }, - }) + } + + expect(results).to.eql(expected) expect(resultOrder).to.eql(["a", "b", "c", "d"]) + + expect(callbackResults).to.eql({ + a: "result-a", + b: "result-b", + c: "result-c", + d: "result-d", + }) }) it.skip( @@ -205,24 +251,24 @@ describe("task-graph", () => { ]) const resultDependencyA = { - result: "result-dependencyA", + output: "result-dependencyA", dependencyResults: {}, } const resultDependencyB = { - result: "result-dependencyB", + output: "result-dependencyB", dependencyResults: {}, } const resultSharedName = { - result: "result-sharedName.2", + output: "result-sharedName.2", dependencyResults: {dependencyA: resultDependencyA, dependencyB: resultDependencyB}, } expect(results).to.eql({ - dependencyA: { result: "result-dependencyA", dependencyResults: {} }, - dependencyB: { result: "result-dependencyB", dependencyResults: {} }, - sharedName: { result: "result-sharedName.2", + dependencyA: { output: "result-dependencyA", dependencyResults: {} }, + dependencyB: { output: "result-dependencyB", dependencyResults: {} }, + sharedName: { output: "result-sharedName.2", dependencyResults: { dependencyA: resultDependencyA, dependencyB: resultDependencyB } }, dependantA: { result: "result-dependantA",