Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Feature/subtasks#340

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
ShMcK merged 11 commits intomasterfromfeature/subtasks
May 18, 2020
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletionsCHANGELOG.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -125,3 +125,33 @@ CODEROAD_TUTORIAL_URL='path/to/tutorial_config_file.json' // will load directly
## [0.6.1]

- Replace checkboxes with icons

## [0.7.0]

- Support loading subtasks (#340). Subtasks are a list of tests that need to pass before a task is complete. They can be loaded by:

1. filtering down to a subset of tests by setting the `step.setup.filter` to a regex pattern that matches the tests you're targeting
2. setting the `step.setup.subtasks` variable to true

- Change for the test runner config. Changes are backwards compatible.

1. `testRunner.path`=> `testRunner.directory`
2. `testRunner.actions` => `testRunner.setup`
3. Change command to capture `args` for "TAP" support, and test "filter"ing support. These changes will help lead to specific test suite presets in the future.

```json
{
"testRunner": {
"command": "mocha",
"args": {
"filter": "--grep",
"tap": "--reporter=mocha-tap-reporter"
},
"directory": ".coderoad",
"setup": {
"commits": ["410bd4f"],
"commands": ["npm install"]
}
}
}
```
2 changes: 1 addition & 1 deletionpackage.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,7 +45,7 @@
"@typescript-eslint/parser": "^2.33.0",
"chokidar": "^3.4.0",
"dotenv": "^8.2.0",
"eslint": "^7.0.0",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.3",
"git-url-parse": "^11.1.2",
Expand Down
6 changes: 3 additions & 3 deletionssrc/actions/setupActions.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,10 +10,10 @@ import logger from '../services/logger'
interfaceSetupActions{
actions:TT.StepActions
send:(action:T.Action)=>void// send messages to client
path?:string
dir?:string
}

exportconstsetupActions=async({ actions, send,path}:SetupActions):Promise<void>=>{
exportconstsetupActions=async({ actions, send,dir}:SetupActions):Promise<void>=>{
if(!actions){
return
}
Expand DownExpand Up@@ -45,7 +45,7 @@ export const setupActions = async ({ actions, send, path }: SetupActions): Promi

// 4. run command
if(!alreadyLoaded){
awaitrunCommands({commands:commands||[], send,path}).catch(onError)
awaitrunCommands({commands:commands||[], send,dir}).catch(onError)
}
}

Expand Down
10 changes: 5 additions & 5 deletionssrc/actions/tutorialConfig.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,12 +6,12 @@ import * as git from '../services/git'
import { DISABLE_RUN_ON_SAVE } from '../environment'

interface TutorialConfigParams {
config: TT.TutorialConfig
data: TT.Tutorial
alreadyConfigured?: boolean
onComplete?(): void
}

const tutorialConfig = async ({config, alreadyConfigured }: TutorialConfigParams): Promise<E.ErrorMessage | void> => {
const tutorialConfig = async ({data, alreadyConfigured }: TutorialConfigParams): Promise<E.ErrorMessage | void> => {
if (!alreadyConfigured) {
// setup git, add remote
const initError: E.ErrorMessage | void = await git.initIfNotExists().catch(
Expand All@@ -27,7 +27,7 @@ const tutorialConfig = async ({ config, alreadyConfigured }: TutorialConfigParam
}

// verify that internet is connected, remote exists and branch exists
const remoteConnectError: E.ErrorMessage | void = await git.checkRemoteConnects(config.repo).catch(
const remoteConnectError: E.ErrorMessage | void = await git.checkRemoteConnects(data.config.repo).catch(
(error: Error): E.ErrorMessage => ({
type: 'FailedToConnectToGitRepo',
message: error.message,
Expand All@@ -40,7 +40,7 @@ const tutorialConfig = async ({ config, alreadyConfigured }: TutorialConfigParam
}

// TODO if remote not already set
const coderoadRemoteError: E.ErrorMessage | void = await git.setupCodeRoadRemote(config.repo.uri).catch(
const coderoadRemoteError: E.ErrorMessage | void = await git.setupCodeRoadRemote(data.config.repo.uri).catch(
(error: Error): E.ErrorMessage => ({
type: 'GitRemoteAlreadyExists',
message: error.message,
Expand All@@ -52,7 +52,7 @@ const tutorialConfig = async ({ config, alreadyConfigured }: TutorialConfigParam
}
}

await vscode.commands.executeCommand(COMMANDS.CONFIG_TEST_RUNNER,config.testRunner)
await vscode.commands.executeCommand(COMMANDS.CONFIG_TEST_RUNNER,data)

if (!DISABLE_RUN_ON_SAVE) {
// verify if file test should run based on document saved
Expand Down
8 changes: 5 additions & 3 deletionssrc/actions/utils/loadWatchers.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,9 +38,11 @@ const loadWatchers = (watchers: string[]) => {
constnow=+newDate()
if(!lastFire||lastFire-now>1000){
vscode.commands.executeCommand(COMMANDS.RUN_TEST,{
onSuccess:()=>{
// cleanup watcher on success
disposeWatcher(watcher)
callbacks:{
onSuccess:()=>{
// cleanup watcher on success
disposeWatcher(watcher)
},
},
})
}
Expand Down
6 changes: 3 additions & 3 deletionssrc/actions/utils/runCommands.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,10 +4,10 @@ import { exec } from '../../services/node'
interfaceRunCommands{
commands:string[]
send:(action:T.Action)=>void
path?:string
dir?:string
}

construnCommands=async({ commands, send,path}:RunCommands)=>{
construnCommands=async({ commands, send,dir}:RunCommands)=>{
if(!commands.length){
return
}
Expand All@@ -19,7 +19,7 @@ const runCommands = async ({ commands, send, path }: RunCommands) => {
send({type:'COMMAND_START',payload:{process:{ ...process,status:'RUNNING'}}})
letresult:{stdout:string;stderr:string}
try{
result=awaitexec({ command,path})
result=awaitexec({ command,dir})
console.log(result)
}catch(error){
console.log(`Test failed:${error.message}`)
Expand Down
9 changes: 4 additions & 5 deletionssrc/channel/index.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -204,7 +204,7 @@ class Channel implements Channel {
}
}

const error: E.ErrorMessage | void = await tutorialConfig({config:data.config }).catch((error: Error) => ({
const error: E.ErrorMessage | void = await tutorialConfig({ data }).catch((error: Error) => ({
type: 'UnknownError',
message: `Location: tutorial config.\n\n${error.message}`,
}))
Expand All@@ -231,9 +231,8 @@ class Channel implements Channel {
if (!tutorialContinue) {
throw new Error('Invalid tutorial to continue')
}
const continueConfig: TT.TutorialConfig = tutorialContinue.config
await tutorialConfig({
config: continueConfig,
data: tutorialContinue,
alreadyConfigured: true,
})
// update the current stepId on startup
Expand DownExpand Up@@ -307,7 +306,7 @@ class Channel implements Channel {
await vscode.commands.executeCommand(COMMANDS.SET_CURRENT_POSITION, action.payload.position)
await solutionActions({ actions: action.payload.actions, send: this.send })
// run test following solution to update position
vscode.commands.executeCommand(COMMANDS.RUN_TEST)
vscode.commands.executeCommand(COMMANDS.RUN_TEST, { subtasks: true })
return
case 'EDITOR_SYNC_PROGRESS':
// update progress when a level is deemed complete in the client
Expand All@@ -318,7 +317,7 @@ class Channel implements Channel {
await showOutput(channel)
return
case 'EDITOR_RUN_TEST':
vscode.commands.executeCommand(COMMANDS.RUN_TEST)
vscode.commands.executeCommand(COMMANDS.RUN_TEST, action?.payload)
return
default:
logger(`No match for action type: ${actionType}`)
Expand Down
25 changes: 19 additions & 6 deletionssrc/editor/commands.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,13 +50,19 @@ export const createCommands = ({ extensionPath, workspaceState }: CreateCommandP
// setup 1x1 horizontal layout
webview.createOrShow()
},
[COMMANDS.CONFIG_TEST_RUNNER]: async (config: TT.TutorialTestRunnerConfig) => {
if (config.actions) {
[COMMANDS.CONFIG_TEST_RUNNER]: async (data: TT.Tutorial) => {
const testRunnerConfig = data.config.testRunner
const setup = testRunnerConfig.setup || testRunnerConfig.actions // TODO: deprecate and remove config.actions
if (setup) {
// setup tutorial test runner commits
// assumes git already exists
await setupActions({ actions: config.actions, send: webview.send, path: config.path })
await setupActions({
actions: setup,
send: webview.send,
dir: testRunnerConfig.directory || testRunnerConfig.path,
}) // TODO: deprecate and remove config.path
}
testRunner = createTestRunner(config, {
testRunner = createTestRunner(data, {
onSuccess: (position: T.Position) => {
logger('test pass position', position)
// send test pass message back to client
Expand All@@ -75,20 +81,27 @@ export const createCommands = ({ extensionPath, workspaceState }: CreateCommandP
// send test run message back to client
webview.send({ type: 'TEST_RUNNING', payload: { position } })
},
onLoadSubtasks: ({ summary }) => {
webview.send({ type: 'LOAD_TEST_SUBTASKS', payload: { summary } })
},
})
},
[COMMANDS.SET_CURRENT_POSITION]: (position: T.Position) => {
// set from last setup stepAction
currentPosition = position
},
[COMMANDS.RUN_TEST]: (callback?: { onSuccess: () => void }) => {
[COMMANDS.RUN_TEST]: ({
subtasks,
callbacks,
}: { subtasks?: boolean; callbacks?: { onSuccess: () => void } } = {}) => {
logger('run test current', currentPosition)
// use stepId from client, or last set stepId
// const position: T.Position = {
// ...current,
// stepId: current && current.position.stepId?.length ? current.position.stepId : currentPosition.stepId,
// }
testRunner(currentPosition, callback?.onSuccess)
logger('currentPosition', currentPosition)
testRunner({ position: currentPosition, onSuccess: callbacks?.onSuccess, subtasks })
},
}
}
4 changes: 2 additions & 2 deletionssrc/services/node/index.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,11 +8,11 @@ const asyncExec = promisify(cpExec)

interfaceExecParams{
command:string
path?:string
dir?:string
}

exportconstexec=(params:ExecParams):Promise<{stdout:string;stderr:string}>|never=>{
constcwd=join(WORKSPACE_ROOT,params.path||'')
constcwd=join(WORKSPACE_ROOT,params.dir||'')
returnasyncExec(params.command,{ cwd})
}

Expand Down
55 changes: 48 additions & 7 deletionssrc/services/testRunner/index.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,7 @@ import * as T from 'typings'
import * as TT from 'typings/tutorial'
import { exec } from '../node'
import logger from '../logger'
import parser from './parser'
import parser, { ParserOutput } from './parser'
import { debounce, throttle } from './throttle'
import onError from '../sentry/onError'
import { clearOutput, addOutput } from './output'
Expand All@@ -13,14 +13,22 @@ interface Callbacks {
onFail(position: T.Position, failSummary: T.TestFail): void
onRun(position: T.Position): void
onError(position: T.Position): void
onLoadSubtasks({ summary }: { summary: { [testName: string]: boolean } }): void
}

const failChannelName = 'CodeRoad (Tests)'
const logChannelName = 'CodeRoad (Logs)'

const createTestRunner = (config: TT.TutorialTestRunnerConfig, callbacks: Callbacks) => {
return async (position: T.Position, onSuccess?: () => void): Promise<void> => {
logger('createTestRunner', position)
interface TestRunnerParams {
position: T.Position
subtasks?: boolean
onSuccess?: () => void
}

const createTestRunner = (data: TT.Tutorial, callbacks: Callbacks) => {
const testRunnerConfig = data.config.testRunner
const testRunnerFilterArg = testRunnerConfig.args?.filter
return async ({ position, onSuccess, subtasks }: TestRunnerParams): Promise<void> => {
const startTime = throttle()
// throttle time early
if (!startTime) {
Expand All@@ -30,11 +38,35 @@ const createTestRunner = (config: TT.TutorialTestRunnerConfig, callbacks: Callba
logger('------------------- RUN TEST -------------------')

// flag as running
callbacks.onRun(position)
if (!subtasks) {
callbacks.onRun(position)
}

let result: { stdout: string | undefined; stderr: string | undefined }
try {
result = await exec({ command: config.command, path: config.path })
let command = testRunnerConfig.args
? `${testRunnerConfig.command} ${testRunnerConfig?.args.tap}`
: testRunnerConfig.command // TODO: enforce TAP

// filter tests if requested
if (testRunnerFilterArg) {
// get tutorial step from position
// check the step actions for specific command
// NOTE: cannot just pass in step actions as the test can be called by:
// - onEditorSave, onWatcher, onSolution, onRunTest, onSubTask
const levels = data.levels
const level = levels.find((l) => l.id === position.levelId)
const step = level?.steps.find((s) => s.id === position.stepId)
const testFilter = step?.setup?.filter
if (testFilter) {
// append filter commands
command = [command, testRunnerFilterArg, testFilter].join(' ')
} else {
throw new Error('Test Runner filter not configured')
}
}
logger('COMMAND', command)
result = await exec({ command, dir: testRunnerConfig.directory || testRunnerConfig.path }) // TODO: remove config.path later
} catch (err) {
result = { stdout: err.stdout, stderr: err.stack }
}
Expand All@@ -49,7 +81,13 @@ const createTestRunner = (config: TT.TutorialTestRunnerConfig, callbacks: Callba

const { stdout, stderr } = result

const tap = parser(stdout || '')
const tap: ParserOutput = parser(stdout || '')

if (subtasks) {
callbacks.onLoadSubtasks({ summary: tap.summary })
// exit early
return
}

addOutput({ channel: logChannelName, text: tap.logs.join('\n'), show: false })

Expand All@@ -60,6 +98,7 @@ const createTestRunner = (config: TT.TutorialTestRunnerConfig, callbacks: Callba
const failSummary = {
title: firstFail.message || 'Test Failed',
description: firstFail.details || 'Unknown error',
summary: tap.summary,
}
callbacks.onFail(position, failSummary)
const output = formatFailOutput(tap)
Expand All@@ -76,7 +115,9 @@ const createTestRunner = (config: TT.TutorialTestRunnerConfig, callbacks: Callba
// PASS
if (tap.ok) {
clearOutput(failChannelName)

callbacks.onSuccess(position)

if (onSuccess) {
onSuccess()
}
Expand Down
16 changes: 15 additions & 1 deletionsrc/services/testRunner/parser.test.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,13 @@ describe('parser', () => {
1..1
ok 1 - Should pass
`
expect(parser(example)).toEqual({ok:true,passed:[{message:'Should pass'}],failed:[],logs:[]})
expect(parser(example)).toEqual({
ok:true,
passed:[{message:'Should pass'}],
failed:[],
logs:[],
summary:{'Should pass':true},
})
})
test('should detect multiple successes',()=>{
constexample=`
Expand All@@ -20,6 +26,10 @@ ok 2 - Should also pass
passed:[{message:'Should pass'},{message:'Should also pass'}],
failed:[],
logs:[],
summary:{
'Should pass':true,
'Should also pass':true,
},
})
})
test('should detect failure if no tests passed',()=>{
Expand DownExpand Up@@ -170,6 +180,10 @@ at processImmediate (internal/timers.js:439:21)`,
},
],
logs:['log 1','log 2'],
summary:{
'package.json should have "express" installed':true,
'server should log "Hello World"':false,
},
})
})
})
Loading

[8]ページ先頭

©2009-2025 Movatter.jp