Skip to content

Commit

Permalink
improvement(core): add get tests command
Browse files Browse the repository at this point in the history
  • Loading branch information
eysi09 authored and thsig committed Jul 7, 2021
1 parent 4d65cb2 commit d610afd
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 55 deletions.
58 changes: 4 additions & 54 deletions core/src/commands/get/get-tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import chalk from "chalk"
import indentString from "indent-string"
import { sortBy, omit, uniq } from "lodash"
import { sortBy, uniq } from "lodash"
import { Command, CommandResult, CommandParams } from "../base"
import { printHeader } from "../../logger/util"
import { GardenTask } from "../../types/task"
import { StringsParameter } from "../../cli/params"
import { makeGetTestOrTaskLog, makeGetTestOrTaskResult } from "../helpers"

const getTasksArgs = {
tasks: new StringsParameter({
Expand All @@ -22,34 +20,6 @@ const getTasksArgs = {

type Args = typeof getTasksArgs

export function prettyPrintTask(task: GardenTask): string {
let out = `${chalk.cyan.bold(task.name)}`

if (task.spec.args || task.spec.args === null) {
out += "\n" + indentString(printField("args", task.spec.args), 2)
} else {
out += "\n" + indentString(printField("command", task.spec.command), 2)
}

if (task.spec.description) {
out += "\n" + indentString(printField("description", task.spec.description), 2)
}

if (task.config.dependencies.length) {
out += "\n" + indentString(`${chalk.gray("dependencies")}:`, 2) + "\n"
out += indentString(task.config.dependencies.map((depName) => `• ${depName}`).join("\n"), 4)
out += "\n"
} else {
out += "\n"
}

return out
}

function printField(name: string, value: string | null) {
return `${chalk.gray(name)}: ${value || ""}`
}

export class GetTasksCommand extends Command<Args> {
name = "tasks"
help = "Lists the tasks defined in your project's modules."
Expand All @@ -66,30 +36,10 @@ export class GetTasksCommand extends Command<Args> {
const taskModuleNames = uniq(tasks.map((t) => t.module.name))
const modules = sortBy(graph.getModules({ names: taskModuleNames }), (m) => m.name)

const taskListing: any[] = []
let logStr = ""

for (const m of modules) {
const tasksForModule = sortBy(
tasks.filter((t) => t.module.name === m.name),
(t) => t.name
)

const logStrForTasks = tasksForModule.map((t) => indentString(prettyPrintTask(t), 2)).join("\n")

logStr += `tasks in module ${chalk.green(m.name)}` + "\n" + logStrForTasks + "\n"

taskListing.push({
[m.name]: tasksForModule.map((t) => ({
...omit(t.config.spec, ["timeout"]),
name: t.name,
description: t.config.spec.description,
dependencies: t.config.spec.dependencies,
})),
})
}
const taskListing = makeGetTestOrTaskResult(modules, tasks)

if (taskListing.length > 0) {
const logStr = makeGetTestOrTaskLog(modules, tasks, "tasks")
log.info(logStr.trim())
} else {
log.info(`No tasks defined for project ${garden.projectName}`)
Expand Down
50 changes: 50 additions & 0 deletions core/src/commands/get/get-tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2018-2021 Garden Technologies, Inc. <[email protected]>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { sortBy, uniq } from "lodash"
import { Command, CommandResult, CommandParams } from "../base"
import { printHeader } from "../../logger/util"
import { StringsParameter } from "../../cli/params"
import { makeGetTestOrTaskLog, makeGetTestOrTaskResult } from "../helpers"

const getTestsArgs = {
tests: new StringsParameter({
help: "Specify tests(s) to list. Use comma as a separator to specify multiple tests.",
}),
}

type Args = typeof getTestsArgs

export class GetTestsCommand extends Command<Args> {
name = "tests"
help = "Lists the tests defined in your project's modules."

arguments = getTestsArgs

printHeader({ headerLog }) {
printHeader(headerLog, "Tests", "open_book")
}

async action({ args, garden, log }: CommandParams<Args>): Promise<CommandResult> {
const graph = await garden.getConfigGraph(log)
const tests = graph.getTests({ names: args.tests })
const testModuleNames = uniq(tests.map((t) => t.module.name))
const modules = sortBy(graph.getModules({ names: testModuleNames }), (m) => m.name)

const testListing = makeGetTestOrTaskResult(modules, tests)

if (testListing.length > 0) {
const logStr = makeGetTestOrTaskLog(modules, tests, "tests")
log.info(logStr.trim())
} else {
log.info(`No tests defined for project ${garden.projectName}`)
}

return { result: testListing }
}
}
2 changes: 2 additions & 0 deletions core/src/commands/get/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { GetOutputsCommand } from "./get-outputs"
import { GetDoddiCommand } from "./get-doddi"
import { GetModulesCommand } from "./get-modules"
import { GetVaccineCommand } from "./get-vaccine"
import { GetTestsCommand } from "./get-tests"

export class GetCommand extends CommandGroup {
name = "get"
Expand All @@ -37,6 +38,7 @@ export class GetCommand extends CommandGroup {
GetSecretCommand,
GetStatusCommand,
GetTasksCommand,
GetTestsCommand,
GetTaskResultCommand,
GetTestResultCommand,
GetDebugInfoCommand,
Expand Down
70 changes: 70 additions & 0 deletions core/src/commands/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import chalk from "chalk"
import indentString from "indent-string"
import { sortBy } from "lodash"

import { ConfigGraph } from "../config-graph"
import { GardenModule } from "../types/module"
import { GardenService } from "../types/service"
import { GardenTask } from "../types/task"
import { GardenTest } from "../types/test"

export async function getDevModeServiceNames(namesFromOpt: string[] | undefined, configGraph: ConfigGraph) {
const names = namesFromOpt || []
Expand Down Expand Up @@ -56,3 +63,66 @@ export async function validateHotReloadServiceNames(
function supportsHotReloading(service: GardenService) {
return service.config.hotReloadable
}

export function makeGetTestOrTaskResult(modules: GardenModule[], testsOrTasks: GardenTest[] | GardenTask[]) {
return modules.map((m) => {
const testsOrTasksForModule = sortBy(
testsOrTasks.filter((t) => t.module.name === m.name),
(t) => t.name
)

return {
[m.name]: testsOrTasksForModule.map((t) => ({
...t.config.spec,
name: t.name,
})),
}
})
}

export function makeGetTestOrTaskLog(
modules: GardenModule[],
testsOrTasks: GardenTest[] | GardenTask[],
type: "tests" | "tasks"
) {
let logStr = ""
for (const m of modules) {
const enitities = sortBy(
testsOrTasks.filter((t) => t.module.name === m.name),
(t) => t.name
)

const logStrForTasks = enitities.map((t) => indentString(prettyPrintTestOrTask(t), 2)).join("\n")

logStr += `${type} in module ${chalk.green(m.name)}` + "\n" + logStrForTasks + "\n"
}
return logStr
}

function prettyPrintTestOrTask(testOrTask: GardenTask | GardenTest): string {
let out = `${chalk.cyan.bold(testOrTask.name)}`

if (testOrTask.spec.args || testOrTask.spec.args === null) {
out += "\n" + indentString(printField("args", testOrTask.spec.args), 2)
} else {
out += "\n" + indentString(printField("command", testOrTask.spec.command), 2)
}

if (testOrTask.spec.description) {
out += "\n" + indentString(printField("description", testOrTask.spec.description), 2)
}

if (testOrTask.config.dependencies.length) {
out += "\n" + indentString(`${chalk.gray("dependencies")}:`, 2) + "\n"
out += indentString(testOrTask.config.dependencies.map((depName) => `• ${depName}`).join("\n"), 4)
out += "\n"
} else {
out += "\n"
}

return out
}

function printField(name: string, value: string | null) {
return `${chalk.gray(name)}: ${value || ""}`
}
17 changes: 16 additions & 1 deletion core/src/config-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { TaskConfig } from "./config/task"
import { makeTestTaskName } from "./tasks/helpers"
import { TaskType, makeBaseKey } from "./tasks/base"
import { ModuleTypeMap } from "./types/plugin/plugin"
import { testFromModule, GardenTest } from "./types/test"
import { testFromModule, GardenTest, testFromConfig } from "./types/test"

// Each of these types corresponds to a Task class (e.g. BuildTask, DeployTask, ...).
export type DependencyGraphNodeType = "build" | "deploy" | "run" | "test"
Expand Down Expand Up @@ -359,6 +359,21 @@ export class ConfigGraph {
return configs.map((c) => taskFromConfig(this.getModule(c.moduleKey, true), c.config))
}

/**
* Returns all tests defined in this configuration graph, or the ones specified.
* Note that test names are not unique, so a given name can return multiple tests.
*/
getTests({ names, includeDisabled = false }: { names?: string[]; includeDisabled?: boolean } = {}) {
const testConfigs = includeDisabled ? this.testConfigs : pickBy(this.testConfigs, (t) => !this.isDisabled(t))

// We need to filter by full test name, i.e <module-name>.<test-name>
const fullTestNames = names ? Object.keys(testConfigs).filter((name) => names.includes(name.split(".")[1])) : names

const configs = Object.values(fullTestNames ? pickKeys(testConfigs, fullTestNames, "test") : testConfigs)

return configs.map((c) => testFromConfig(this.getModule(c.moduleKey, true), c.config, this))
}

/*
* If filter is provided to any of the methods below that accept it, matching nodes
* (and their dependencies/dependants, if recursive = true) are ignored.
Expand Down
Loading

0 comments on commit d610afd

Please sign in to comment.