Commit 281a350f authored by Jakob Moser's avatar Jakob Moser
Browse files

Merge branch 'await-command-completion' into 'master'

Await command completion

See merge request !17
parents 2679d7e6 34cddd47
Loading
Loading
Loading
Loading
+11 −16
Original line number Diff line number Diff line
@@ -154,13 +154,13 @@ class ExerciseExecutionContext {
    prepareWith(command, keepVisible) {
        return new Promise((resolve, _) => {

            function prepareAndResolve() {
            async function prepareAndResolve() {
                // Actually prepare and resolve the promise (we get the resolve() function via the closure)
                if(command !== null) {
                    runCommand(command)
                    await runCommand(command)
                }
                if (!keepVisible) {
                    runCommand("clear")
                    await runCommand("clear")
                }
                resolve()
            }
@@ -174,7 +174,6 @@ class ExerciseExecutionContext {
                    if (getTerminalContents().length > 0) {
                        clearInterval(intervalId)
                        prepareAndResolve()
                        resolve()
                    }
                }, 500)
            }
@@ -229,9 +228,9 @@ class ExerciseExecutionContext {
     * @returns An object that has the method "hasOutput"
     */
    verify(command) {
        if(command) {
            runCommand(command)
        }
        // If a command is provided, store the returned promise (which is resolved as soon as the command was run),
        // if no command is provided, directly start with a resolved promise.
        const commandPromise = command ? runCommand(command) : Promise.resolve()
        const verifyHandler = this._verifyHandler

        return {
@@ -246,9 +245,8 @@ class ExerciseExecutionContext {
             * @param {Function|String|Object} param String or list of strings to exactly match, or predicate function
             */
            hasOutput(param) {
                // Wait for 200ms to ensure that the VM has processed the input and written some
                // output to the terminal
                setTimeout(() => {
                // Wait until the VM has processed the input and written some output to the terminal
                commandPromise.then(() => {
                    const terminalContents = getTerminalContents()
                    const latestEntry = terminalContents[terminalContents.length - 1]

@@ -267,7 +265,7 @@ class ExerciseExecutionContext {
                    const result = !!predicate(latestEntry.output)

                    verifyHandler(result)
                }, 200)
                })
            },

            /**
@@ -280,13 +278,10 @@ class ExerciseExecutionContext {
             * @param {Function|String} param String to exactly match, or predicate function
             */
            commandWas(param) {
                // Wait for 200ms to ensure that the VM has processed the input and written some
                // output to the terminal
                setTimeout(() => {
                commandPromise.then(() => {
                    const terminalContents = getTerminalContents()
                    const latestEntry = terminalContents[terminalContents.length - 1]


                    // Poor man's switch-case: depending on the "typeof param", a different verification predicate is chosen
                    const predicate = {
                        "function": param,
@@ -296,7 +291,7 @@ class ExerciseExecutionContext {
                    const result = !!predicate(latestEntry.input)

                    verifyHandler(result)
                }, 200)
                })
            }
        }
    }
+30 −2
Original line number Diff line number Diff line
@@ -108,9 +108,10 @@ function sanitize(line) {
 *   input line), the returned results won't contain all the output or the input line
 * - Empty lines are ignored: Even if a command legitimately outputs an empty line, it will be ignored.
 * 
 * @param includeLastInteraction If the last interaction (which only consists of a prompt, with nothing executed yet) should be included
 * @returns List of objects of the form {"prompt": "...", "input": "...", "output": ["...", "..."]}
 */
export function getTerminalContents() {
export function getTerminalContents(includeLastInteraction) {
    const contents = []
    let currentInteraction = {}

@@ -128,9 +129,33 @@ export function getTerminalContents() {
            }
        })

    if(includeLastInteraction) {
        contents.push(currentInteraction)
    }

    return contents
}

/**
 * Returns the current prompt as soon as one is shown
 */
function prompt() {
    return new Promise((resolve)=>{
        const intervalId = setInterval(checkAndResolve, 100)
    
        function checkAndResolve() {
            const lastInteraction = getTerminalContents(true).at(-1)

            if(lastInteraction.prompt && !lastInteraction.input.trim()) {
                clearInterval(intervalId)
                resolve(prompt)
            }
        }

        checkAndResolve()
    })
}

/**
 * Calls the given handler function without parameters whenever the user presses the key enter
 * @param {Function} handler A handler function
@@ -148,7 +173,7 @@ export function onEnterPressed(handler) {
 * The command will then be executed, and the output will be shown in the terminal
 * @param {string} command A command to execute
 */
export function runCommand(command) {
export async function runCommand(command) {
    const textarea = document.querySelector(".term_textarea")
    // We append \n to simulate the Enter press
    textarea.value = command + "\n"
@@ -156,6 +181,9 @@ export function runCommand(command) {
    // Now we just have to trigger an input event to activate the JSLinux event handler, see
    // https://thewebdev.info/2021/05/02/how-to-programmatically-trigger-a-change-event-on-an-input-with-javascript/
    textarea.dispatchEvent(new Event("input"))

    // And waid until a prompt is shown (-> until the command is executed)
    await prompt()
}

/**