feat(web): re-enable web exports

This commit is contained in:
Leroy Hopson 2024-03-30 15:24:32 +13:00 committed by Leroy Hopson
parent fcad5d64b9
commit ff95b60a56
26 changed files with 940 additions and 4887 deletions

View file

@ -173,16 +173,20 @@ jobs:
addons/godot_xterm/native/bin/*.xcframework addons/godot_xterm/native/bin/*.xcframework
addons/godot_xterm/native/bin/*.dll addons/godot_xterm/native/bin/*.dll
html5-export: web-export:
name: "HTML5 Export" name: "Web Export"
needs: [check-archive, check-pre-commit]
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- id: install-chrome
uses: browser-actions/setup-chrome@v1
with:
chrome-version: 121
- name: Setup Godot - name: Setup Godot
uses: lihop/setup-godot@v2 uses: lihop/setup-godot@v2
with: with:
version: "4.2.1-stable" version: "4.2.1-stable"
export-templates: true
- name: Import assets - name: Import assets
run: godot --editor --headless --quit-after 100 || true run: godot --editor --headless --quit-after 100 || true
- name: Wait for build - name: Wait for build
@ -196,41 +200,28 @@ jobs:
with: with:
path: addons/godot_xterm/native/bin path: addons/godot_xterm/native/bin
merge-multiple: true merge-multiple: true
# Some remaining steps temporarily disabled. - name: Export for web
- name: Export HTML5
if: false
uses: nick-fields/retry@v3 uses: nick-fields/retry@v3
with: with:
command: godot --no-window --export HTML5 command: godot --no-window --export-release Web
retry_on: error retry_on: error
timeout_minutes: 5 timeout_minutes: 5
max_attempts: 6 max_attempts: 6
- name: NPM cache - name: NPM cache
uses: c-hive/gha-npm-cache@v1 uses: c-hive/gha-npm-cache@v1
with: with:
directory: test/html5 directory: test/web
- name: Smoke test HTML5 export - name: Smoke test Web export
if: false
shell: bash shell: bash
working-directory: test/html5 working-directory: test/web
run: | run: |
export GODOT_XTERM_CHROME_PATH=${{ steps.install-chrome.outputs.chrome-path }}
npm ci npm ci
npx serve ../../docs/demo -p 3000 & npm test
npx cypress run
- name: Upload cypress artifacts (on failure)
uses: actions/upload-artifact@v4
if: false
#if: ${{ failure() }}
with:
name: cypress-artifacts
path: |-
test/html5/cypress/screenshots
test/html5/cypress/videos
- name: Upload export - name: Upload export
if: false
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: html5-demo name: web-demo
path: docs/demo path: docs/demo
test: test:
@ -316,7 +307,7 @@ jobs:
merge-artifacts: merge-artifacts:
name: Merge Artifacts name: Merge Artifacts
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build-linux, build-other-platforms, test, html5-export] needs: [build-linux, build-other-platforms, test, web-export]
strategy: strategy:
matrix: matrix:
target: [release, debug] target: [release, debug]

2
.gitignore vendored
View file

@ -7,7 +7,7 @@ export.cfg
export/ export/
export_presets.cfg export_presets.cfg
# Dummy HTML5 export presets file for continuous integration # Dummy Web export presets file for continuous integration
!.github/dist/export_presets.cfg !.github/dist/export_presets.cfg
# Mono-specific ignores # Mono-specific ignores

View file

@ -8,6 +8,11 @@ godot := `echo "${GODOT:-godot} --rendering-driver ${RENDERING_DRIVER:-vulkan}"`
build: build:
cd addons/godot_xterm/native && scons debug_symbols=yes cd addons/godot_xterm/native && scons debug_symbols=yes
build-javascript:
UID_GID="$(id -u):$(id -g)" docker-compose -f addons/godot_xterm/native/docker-compose.yml run --rm javascript
build-all: build build-javascript
install: install:
{{godot}} --headless -s plug.gd install {{godot}} --headless -s plug.gd install

View file

@ -11,10 +11,7 @@ services:
- /bin/bash - /bin/bash
- -c - -c
- | - |
cd /src/thirdparty/godot-cpp scons platform=web target=$${TARGET:-template_debug} -j$$(nproc)
scons platform=javascript target=$${TARGET:-debug} -j$$(nproc)
cd /src
scons platform=javascript target=$${TARGET:-debug} -j$$(nproc)
libuv-linux: libuv-linux:
user: ${UID_GID} user: ${UID_GID}
build: build:

View file

@ -9,6 +9,8 @@ linux.debug.x86_64 = "res://addons/godot_xterm/native/bin/libgodot-xterm.linux.t
linux.release.x86_64 = "res://addons/godot_xterm/native/bin/libgodot-xterm.linux.template_release.x86_64.so" linux.release.x86_64 = "res://addons/godot_xterm/native/bin/libgodot-xterm.linux.template_release.x86_64.so"
linux.debug.x86_32 = "res://addons/godot_xterm/native/bin/libgodot-xterm.linux.template_debug.x86_32.so" linux.debug.x86_32 = "res://addons/godot_xterm/native/bin/libgodot-xterm.linux.template_debug.x86_32.so"
linux.release.x86_32 = "res://addons/godot_xterm/native/bin/libgodot-xterm.linux.template_release.x86_32.so" linux.release.x86_32 = "res://addons/godot_xterm/native/bin/libgodot-xterm.linux.template_release.x86_32.so"
web.debug.wasm32 = "res://addons/godot_xterm/native/bin/libgodot-xterm.web.template_debug.wasm32.wasm"
web.release.wasm32 = "res://addons/godot_xterm/native/bin/libgodot-xterm.web.template_release.wasm32.wasm"
macos.debug = "res://addons/godot_xterm/native/bin/libgodot-xterm.macos.template_debug.framework" macos.debug = "res://addons/godot_xterm/native/bin/libgodot-xterm.macos.template_debug.framework"
macos.release = "res://addons/godot_xterm/native/bin/libgodot-xterm.macos.template_release.framework" macos.release = "res://addons/godot_xterm/native/bin/libgodot-xterm.macos.template_release.framework"
windows.debug.x86_64 = "res://addons/godot_xterm/native/bin/libgodot-xterm.windows.template_debug.x86_64.dll" windows.debug.x86_64 = "res://addons/godot_xterm/native/bin/libgodot-xterm.windows.template_debug.x86_64.dll"

View file

@ -10,7 +10,7 @@
GDNative terminal for Godot. GDNative terminal for Godot.
Built using [libtsm](https://www.freedesktop.org/wiki/Software/libtsm/), [libuv](https://github.com/libuv/libuv), and [node-pty](https://github.com/microsoft/node-pty). Built using [libtsm](https://www.freedesktop.org/wiki/Software/libtsm/), [libuv](https://github.com/libuv/libuv), and [node-pty](https://github.com/microsoft/node-pty).
Primarily developed and tested on Linux, it also supports macOS with partial support for Windows and HTML5. See the [Features section](introduction.md#features) for more detail. Primarily developed and tested on Linux, it also supports macOS with partial support for Windows and Web. See the [Features section](introduction.md#features) for more detail.
<video width="100%" controls> <video width="100%" controls>
<source src="https://user-images.githubusercontent.com/3696783/126894061-a69eb6ad-9979-4723-ade7-829494a9fc87.mp4" /> <source src="https://user-images.githubusercontent.com/3696783/126894061-a69eb6ad-9979-4723-ade7-829494a9fc87.mp4" />

View file

@ -51,7 +51,13 @@ See the [Setup section](/setup/index.md) for more details.
Other examples of GodotXterm usage are available online: Other examples of GodotXterm usage are available online:
- [**Live Demo**](https://lihop.github.io/godot-xterm-dist/demo): An HTML5 export of the GodotXterm repo's example scenes. - [**Live Demo**](https://lihop.github.io/godot-xterm-dist/demo): A Web export of the GodotXterm repo's example scenes.
:::{error}
The Web export made with the latest version of the plugin has been verified to work with Google's Chrome browser, and known not to work with Firefox.
Some aspects of this may be due to the poor compatibility of Godot 4's Web exports with various browsers.
If you are wanting to export to web, consider using an older version of the plugin built for Godot 3.
:::
- [**The Guest**](https://lihop.itch.io/the-guest): A Linux only submission to the [Linux Game Jam 2022](https://itch.io/jam/linux-game-jam-2022). Uses GodotXterm in conjunction with the [gdtemu addon](https://github.com/lihop/gdtemu) and Linux's Kernel-based Virtual Machine, to provide the player with an interactive VM that can be used in the game world. - [**The Guest**](https://lihop.itch.io/the-guest): A Linux only submission to the [Linux Game Jam 2022](https://itch.io/jam/linux-game-jam-2022). Uses GodotXterm in conjunction with the [gdtemu addon](https://github.com/lihop/gdtemu) and Linux's Kernel-based Virtual Machine, to provide the player with an interactive VM that can be used in the game world.

View file

@ -48,6 +48,6 @@ Also feel free to open a new discussion in the [discussions](https://github.com/
## Cross Compiling ## Cross Compiling
Although the SConstruct file contains some logic for cross-compiling, it has never been tested. If you want compile for other platforms consider forking this repo and then pushing your changes to GitHub. The workflow defined in {{ '[main.yml]({}/.github/workflows/main.yml)'.format(repo) }} will run and build the library for all supported platforms (Linux, macOS, Windows, and HTML5). Although the SConstruct file contains some logic for cross-compiling, it has never been tested. If you want compile for other platforms consider forking this repo and then pushing your changes to GitHub. The workflow defined in {{ '[main.yml]({}/.github/workflows/main.yml)'.format(repo) }} will run and build the library for all supported platforms (Linux, macOS, Windows, and Web).
Additionally, If you have `docker` and `docker-compose` installed, the {{ '[build.sh]({}/addons/godot_xterm/native/build.sh)'.format(repo) }} script will also try to build the HTML5 binary inside a docker container and copy them to `addons/godot_xterm/native/bin`. Additionally, If you have `docker` and `docker-compose` installed, the {{ '[build.sh]({}/addons/godot_xterm/native/build.sh)'.format(repo) }} script will also try to build the Web binary inside a docker container and copy them to `addons/godot_xterm/native/bin`.

View file

@ -25,7 +25,7 @@ var menu_items := [
"scene": "scene":
( (
preload("../web_console/web_console.tscn") preload("../web_console/web_console.tscn")
if OS.has_feature("JavaScript") if OS.has_feature("web")
else preload("../terminal/terminal.tscn") else preload("../terminal/terminal.tscn")
) )
}, },
@ -157,18 +157,16 @@ func _on_Terminal_key_pressed(data: String, event: InputEventKey) -> void:
) )
return return
var scene = item.scene.instantiate() var scene = item.scene.instantiate()
var pty = scene if OS.has_feature("JavaScript") else scene.get_node("PTY") var pty = scene if OS.has_feature("web") else scene.get_node("PTY")
add_child(scene) add_child(scene)
scene.grab_focus() scene.grab_focus()
await pty.exited await pty.exited
$Terminal.grab_focus() $Terminal.grab_focus()
scene.queue_free() scene.queue_free()
"Exit": "Exit":
pass if OS.has_feature("web"):
# FIXME JavaScriptBridge.eval("window.history.back() || window.close()")
#if OS.has_feature("JavaScript"): get_tree().quit()
#JavaScript.eval("window.history.back() || window.close()")
#get_tree().quit()
func _on_Asciicast_key_pressed( func _on_Asciicast_key_pressed(

View file

@ -8,7 +8,7 @@ var line := ""
var _tput var _tput
@onready var terminal = $Terminal @onready var terminal = $Terminal
@onready var _has_js: bool = OS.has_feature("JavaScript") @onready var _has_js: bool = OS.has_feature("web")
func prompt(prompt: String): func prompt(prompt: String):
@ -42,10 +42,10 @@ func _on_Terminal_key_pressed(_data, event: InputEventKey):
if not event: if not event:
return return
# For some reason, data String is malformed on HTML5, so only use event.unicode. # For some reason, data String is malformed on Web, so only use event.unicode.
var data = char(event.unicode) var data = char(event.unicode)
match event.scancode: match event.keycode:
KEY_ENTER: KEY_ENTER:
terminal.write("\r\n") terminal.write("\r\n")
@ -53,22 +53,20 @@ func _on_Terminal_key_pressed(_data, event: InputEventKey):
return emit_signal("exited", 0) return emit_signal("exited", 0)
if not _has_js: if not _has_js:
var msg := "WebConsole only available in HTML5 build." var msg := "WebConsole only available in Web build."
push_error(msg) push_error(msg)
_tput.setaf(TPut.ANSIColor.red) _tput.setaf(TPut.ANSIColor.red)
terminal.write(msg) terminal.write(msg)
_tput.sgr0() _tput.sgr0()
prompt("\r\n>> ") prompt("\r\n>> ")
else: else:
# TODO: godot4 var json = JavaScriptBridge.eval("JSON.stringify(%s)" % line, true)
pass _tput.setaf(TPut.ANSIColor.magenta)
# var json = JavaScript.eval("JSON.stringify(%s)" % line, true) terminal.write(str(json))
# _tput.setaf(TPut.ANSIColor.magenta) _tput.sgr0()
# terminal.write(str(json))
# _tput.sgr0()
line = "" line = ""
#_tput.srg0() _tput.sgr0()
prompt("\r\n>> ") prompt("\r\n>> ")
KEY_BACKSPACE: KEY_BACKSPACE:

View file

@ -1,5 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://d3xc4utggdchf"] [gd_scene load_steps=3 format=3 uid="uid://d3xc4utggdchf"]
[ext_resource type="Theme" uid="uid://0gk8swmcldbg" path="res://addons/godot_xterm/themes/default.tres" id="1_28w8r"]
[ext_resource type="Script" path="res://examples/web_console/web_console.gd" id="3"] [ext_resource type="Script" path="res://examples/web_console/web_console.gd" id="3"]
[node name="WebConsole" type="Control"] [node name="WebConsole" type="Control"]
@ -9,9 +10,12 @@ anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
theme = ExtResource("1_28w8r")
script = ExtResource("3") script = ExtResource("3")
[node name="Terminal" type="Terminal" parent="."] [node name="Terminal" type="Terminal" parent="."]
layout_mode = 0 layout_mode = 0
anchor_right = 1.0 anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
[connection signal="key_pressed" from="Terminal" to="." method="_on_Terminal_key_pressed"]

View file

@ -1,21 +1,25 @@
[preset.0] [preset.0]
name="HTML5" name="Web"
platform="HTML5" platform="Web"
runnable=true runnable=true
dedicated_server=false
custom_features="" custom_features=""
export_filter="all_resources" export_filter="resources"
include_filter="*.cast, addons/godot_xterm/plugin.cfg, addons/godot_xterm/themes/fonts/hack/hack_regular-3.003.ttf" export_files=PackedStringArray("res://addons/godot_xterm/shaders/foreground.gdshader", "res://addons/godot_xterm/native/godot-xterm.gdextension", "res://addons/godot_xterm/shaders/background.gdshader", "res://addons/godot_xterm/shaders/common.gdshaderinc", "res://examples/web_console/web_console.gd", "res://examples/web_console/web_console.tscn", "res://examples/menu/menu.gd", "res://examples/menu/menu.tscn", "res://examples/asciicast/asciicast.tscn", "res://examples/asciicast/example.cast", "res://addons/godot_xterm/util/tput.gd", "res://examples/terminal/terminal.tscn", "res://examples/terminal/terminal.gd")
exclude_filter="misc/*, **/thirdparty/*, **/unifont/*, **/nerd_fonts/*, **/noto_color_emoji/*, *bold*.ttf, *italic*.ttf" include_filter="addons/godot_xterm/native/bin/*.wasm"
exclude_filter=""
export_path="docs/demo/index.html" export_path="docs/demo/index.html"
script_export_mode=1 encryption_include_filters=""
script_encryption_key="" encryption_exclude_filters=""
encrypt_pck=false
encrypt_directory=false
[preset.0.options] [preset.0.options]
custom_template/debug="" custom_template/debug=""
custom_template/release="" custom_template/release=""
variant/export_type=2 variant/extensions_support=true
vram_texture_compression/for_desktop=true vram_texture_compression/for_desktop=true
vram_texture_compression/for_mobile=false vram_texture_compression/for_mobile=false
html/export_icon=true html/export_icon=true
@ -31,4 +35,4 @@ progressive_web_app/orientation=0
progressive_web_app/icon_144x144="" progressive_web_app/icon_144x144=""
progressive_web_app/icon_180x180="" progressive_web_app/icon_180x180=""
progressive_web_app/icon_512x512="" progressive_web_app/icon_512x512=""
progressive_web_app/background_color=Color( 0, 0, 0, 1 ) progressive_web_app/background_color=Color(0, 0, 0, 1)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View file

@ -1,10 +0,0 @@
const { defineConfig } = require("cypress");
const getCompareSnapshotsPlugin = require("cypress-image-diff-js/dist/plugin");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
getCompareSnapshotsPlugin(on, config);
},
},
});

View file

@ -1,23 +0,0 @@
Cypress.on("window:before:load", (win) => {
cy.spy(win.console, "error");
cy.spy(win.console, "warn");
cy.spy(win.console, "log");
});
describe("GodotXterm HTML5 export", () => {
it("Loads and runs without error", () => {
cy.visit("http://localhost:3000");
cy.compareSnapshot("menu", 0.9, {
limit: 100,
delay: 200,
timeout: 20000,
});
cy.get("body").type("{downArrow}{enter}");
cy.get("body").type('window.console.log("It works!"){enter}');
cy.window().then((win) => {
cy.expect(win.console.error).to.have.callCount(0);
cy.expect(win.console.warn).to.have.callCount(0);
cy.expect(win.console.log).to.be.calledWith("It works!");
});
});
});

View file

@ -1,2 +0,0 @@
import compareSnapshotCommand from "cypress-image-diff-js/dist/command";
compareSnapshotCommand();

View file

@ -1 +0,0 @@
import "./commands";

File diff suppressed because it is too large Load diff

View file

@ -1,11 +0,0 @@
{
"scripts": {
"start": "npx serve ../../docs/demo",
"test": "npx cypress run"
},
"devDependencies": {
"cypress": "^10.4.0",
"cypress-image-diff-js": "^1.21.1",
"serve": "^14.0.1"
}
}

View file

@ -1,14 +0,0 @@
let
pkgs = import <nixpkgs> {};
in pkgs.mkShell {
buildInputs = with pkgs; [
cypress
nodejs-16_x
xsel
];
shellHook = ''
export CYPRESS_INSTALL_BINARY=0
export CYPRESS_RUN_BINARY=${pkgs.cypress}/bin/Cypress
'';
}

788
test/web/package-lock.json generated Normal file
View file

@ -0,0 +1,788 @@
{
"name": "web",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"devDependencies": {
"express": "^4.19.2",
"playwright": "^1.42.1"
}
},
"node_modules/accepts": {
"version": "1.3.8",
"dev": true,
"license": "MIT",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"dev": true,
"license": "MIT"
},
"node_modules/body-parser": {
"version": "1.20.2",
"dev": true,
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/body-parser/node_modules/bytes": {
"version": "3.1.2",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/body-parser/node_modules/debug": {
"version": "2.6.9",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/body-parser/node_modules/ms": {
"version": "2.0.0",
"dev": true,
"license": "MIT"
},
"node_modules/body-parser/node_modules/qs": {
"version": "6.11.0",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/call-bind": {
"version": "1.0.7",
"dev": true,
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/content-type": {
"version": "1.0.5",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.6.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
"dev": true,
"license": "MIT"
},
"node_modules/define-data-property": {
"version": "1.1.4",
"dev": true,
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/depd": {
"version": "2.0.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"dev": true,
"license": "MIT"
},
"node_modules/encodeurl": {
"version": "1.0.2",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/es-define-property": {
"version": "1.0.0",
"dev": true,
"license": "MIT",
"dependencies": {
"get-intrinsic": "^1.2.4"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"dev": true,
"license": "MIT"
},
"node_modules/etag": {
"version": "1.8.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express": {
"version": "4.19.2",
"dev": true,
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.2.0",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
"serve-static": "1.15.0",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/express/node_modules/content-disposition": {
"version": "0.5.4",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express/node_modules/debug": {
"version": "2.6.9",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/express/node_modules/ms": {
"version": "2.0.0",
"dev": true,
"license": "MIT"
},
"node_modules/express/node_modules/path-to-regexp": {
"version": "0.1.7",
"dev": true,
"license": "MIT"
},
"node_modules/express/node_modules/qs": {
"version": "6.11.0",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/express/node_modules/range-parser": {
"version": "1.2.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"statuses": "2.0.1",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/finalhandler/node_modules/debug": {
"version": "2.6.9",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/finalhandler/node_modules/ms": {
"version": "2.0.0",
"dev": true,
"license": "MIT"
},
"node_modules/forwarded": {
"version": "0.2.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.2.4",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gopd": {
"version": "1.0.1",
"dev": true,
"license": "MIT",
"dependencies": {
"get-intrinsic": "^1.1.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"dev": true,
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-proto": {
"version": "1.0.3",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"dev": true,
"license": "MIT",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"dev": true,
"license": "ISC"
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"dev": true,
"license": "MIT"
},
"node_modules/methods": {
"version": "1.1.2",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"dev": true,
"license": "MIT",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"dev": true,
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/negotiator": {
"version": "0.6.3",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/object-inspect": {
"version": "1.13.1",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"dev": true,
"license": "MIT",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/playwright": {
"version": "1.42.1",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.42.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=16"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.42.1",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=16"
}
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"dev": true,
"license": "MIT",
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/raw-body": {
"version": "2.5.2",
"dev": true,
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/raw-body/node_modules/bytes": {
"version": "3.1.2",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"dev": true,
"license": "MIT"
},
"node_modules/send": {
"version": "0.18.0",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "2.0.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/debug": {
"version": "2.6.9",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/send/node_modules/debug/node_modules/ms": {
"version": "2.0.0",
"dev": true,
"license": "MIT"
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"dev": true,
"license": "MIT"
},
"node_modules/send/node_modules/range-parser": {
"version": "1.2.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/serve-static": {
"version": "1.15.0",
"dev": true,
"license": "MIT",
"dependencies": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.18.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/set-function-length": {
"version": "1.2.2",
"dev": true,
"license": "MIT",
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"dev": true,
"license": "ISC"
},
"node_modules/side-channel": {
"version": "1.0.6",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.7",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/statuses": {
"version": "2.0.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.6"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"dev": true,
"license": "MIT",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
}
}
}

9
test/web/package.json Normal file
View file

@ -0,0 +1,9 @@
{
"scripts": {
"test": "node smoke_test.js"
},
"devDependencies": {
"express": "^4.19.2",
"playwright": "^1.42.1"
}
}

11
test/web/shell.nix Normal file
View file

@ -0,0 +1,11 @@
let
pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/23.11.tar.gz") {};
in pkgs.mkShell {
buildInputs = with pkgs; [
nodejs
];
shellHook = ''
export GODOT_XTERM_CHROME_PATH=${pkgs.chromium}/bin/chromium
'';
}

65
test/web/smoke_test.js Normal file
View file

@ -0,0 +1,65 @@
const { chromium } = require("playwright");
const express = require("express");
(async () => {
// Server setup.
const app = express();
const port = 3000;
app.use(
express.static("../../docs/demo", {
setHeaders: (res, path, stat) => {
res.set("Cross-Origin-Embedder-Policy", "require-corp");
res.set("Cross-Origin-Opener-Policy", "same-origin");
},
}),
);
const server = app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}`);
});
// Determine whether to set the executablePath.
const launchOptions = {};
if (process.env.GODOT_XTERM_CHROME_PATH) {
launchOptions.executablePath = process.env.GODOT_XTERM_CHROME_PATH;
}
// Launch the browser with conditional executablePath.
const browser = await chromium.launch(launchOptions);
const page = await browser.newPage();
let loggedMessages = [];
page.on("console", (msg) => {
loggedMessages.push(msg.text());
});
await page.goto(`http://localhost:${port}`);
// TODO: Don't wait for a fixed amount of time, but rather for a baseline screenshot to match.
await page.waitForTimeout(5000);
// Page interaction
await page.keyboard.press("ArrowDown");
await page.keyboard.press("Enter");
await page.keyboard.type('window.console.log("It works!")');
await page.keyboard.press("Enter");
// Close the browser
await browser.close();
const hasItWorkedLog = loggedMessages.some((message) =>
message.includes("It works!"),
);
if (!hasItWorkedLog) {
console.error("Test failed: 'It works!' was not logged to the console.");
} else {
console.log("Test passed: 'It works!' was successfully logged.");
}
// Stop the server
server.close(() => {
console.log("Server stopped");
process.exit(hasItWorkedLog ? 0 : 1);
});
})();