blob: 15606cc8cab39a465b0340f47a3eeeda892d4081 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
window,
Disposable,
QuickInput,
QuickInputButtons,
ExtensionContext,
} from 'vscode'
import { AppConstants } from './utilities/constants'
export async function newTestCaseInput(context: ExtensionContext) {
interface State {
title: string
step: number
totalSteps: number
testName: string
testDesc: string
testModel: string | string
dfdlInfoset: string
dataDocs: string
}
async function collectInputs() {
const state = {} as Partial<State>
await MultiStepInput.run((input) => inputTestName(input, state))
return state as State
}
const title = AppConstants.addNewTitle
async function inputTestName(input: MultiStepInput, state: Partial<State>) {
state.testName = await input.showInputBox({
title,
step: 1,
totalSteps: 5,
value: state.testName || '',
prompt: AppConstants.promptTestName,
validate: validateNotNull,
shouldResume: shouldResume,
})
return (input: MultiStepInput) => inputTestDesc(input, state)
}
async function inputTestDesc(input: MultiStepInput, state: Partial<State>) {
state.testDesc = await input.showInputBox({
title,
step: 2,
totalSteps: 5,
value: state.testDesc || '',
prompt: AppConstants.promptTestDescription,
validate: validateNotNull,
shouldResume: shouldResume,
})
return (input: MultiStepInput) => inputTestModel(input, state)
}
async function inputTestModel(input: MultiStepInput, state: Partial<State>) {
state.testModel = await input.showInputBox({
title,
step: 3,
totalSteps: 5,
value: state.testModel || '',
prompt: AppConstants.promptTestModel,
validate: validateNotNull,
shouldResume: shouldResume,
})
return (input: MultiStepInput) => inputDfdlInfoset(input, state)
}
async function inputDfdlInfoset(
input: MultiStepInput,
state: Partial<State>
) {
state.dfdlInfoset = await input.showInputBox({
title,
step: 4,
totalSteps: 5,
value: state.dfdlInfoset || '',
prompt: AppConstants.promptDfdlInfoset,
validate: validateNotNull,
shouldResume: shouldResume,
})
return (input: MultiStepInput) => inputDataDocuments(input, state)
}
async function inputDataDocuments(
input: MultiStepInput,
state: Partial<State>
) {
state.dataDocs = await input.showInputBox({
title,
step: 5,
totalSteps: 5,
value: state.dataDocs || '',
prompt: AppConstants.prompDataDocuments,
validate: validateNotNull,
shouldResume: shouldResume,
})
}
function shouldResume() {
// Could show a notification with the option to resume.
return new Promise<boolean>((resolve, reject) => {
// noop
})
}
async function validateNotNull(name: string) {
await new Promise((resolve) => setTimeout(resolve, 1000))
return name === '' ? 'Must not be empty' : undefined
}
const state = await collectInputs()
return state
}
// -------------------------------------------------------
// Helper code that wraps the API for the multi-step case.
// -------------------------------------------------------
class InputFlowAction {
static back = new InputFlowAction()
static cancel = new InputFlowAction()
static resume = new InputFlowAction()
}
type InputStep = (input: MultiStepInput) => Thenable<InputStep | void>
interface InputBoxParameters {
title: string
step: number
totalSteps: number
value: string
prompt: string
placeholder?: string
validate: (value: string) => Promise<string | undefined>
shouldResume: () => Thenable<boolean>
}
class MultiStepInput {
static async run<T>(start: InputStep) {
const input = new MultiStepInput()
return input.stepThrough(start)
}
private current?: QuickInput
private steps: InputStep[] = []
private async stepThrough<T>(start: InputStep) {
let step: InputStep | void = start
while (step) {
this.steps.push(step)
if (this.current) {
this.current.enabled = false
this.current.busy = true
}
try {
step = await step(this)
} catch (err) {
if (err === InputFlowAction.back) {
this.steps.pop()
step = this.steps.pop()
} else if (err === InputFlowAction.resume) {
step = this.steps.pop()
} else if (err === InputFlowAction.cancel) {
step = undefined
} else {
throw err
}
}
}
if (this.current) {
this.current.dispose()
}
}
async showInputBox<P extends InputBoxParameters>({
title,
step,
totalSteps,
value,
prompt,
validate,
shouldResume,
placeholder,
}: P) {
const disposables: Disposable[] = []
try {
return await new Promise<
string | (P extends { buttons: (infer I)[] } ? I : never)
>((resolve, reject) => {
const input = window.createInputBox()
input.title = title
input.step = step
input.totalSteps = totalSteps
input.value = value || ''
input.prompt = prompt
input.buttons = [
...(this.steps.length > 1 ? [QuickInputButtons.Back] : []),
]
input.placeholder = placeholder
let validating = validate('')
disposables.push(
input.onDidTriggerButton((item) => {
if (item === QuickInputButtons.Back) {
reject(InputFlowAction.back)
} else {
resolve(<any>item)
}
}),
input.onDidAccept(async () => {
const value = input.value
input.enabled = false
input.busy = true
if (!(await validate(value))) {
resolve(value)
}
input.enabled = true
input.busy = false
}),
input.onDidChangeValue(async (text) => {
const current = validate(text)
validating = current
const validationMessage = await current
if (current === validating) {
input.validationMessage = validationMessage
}
}),
input.onDidHide(() => {
;(async () => {
reject(
shouldResume && (await shouldResume())
? InputFlowAction.resume
: InputFlowAction.cancel
)
})().catch(reject)
})
)
if (this.current) {
this.current.dispose()
}
this.current = input
this.current.show()
})
} finally {
disposables.forEach((d) => d.dispose())
}
}
}