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

2
.gitignore vendored
View file

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

View file

@ -8,6 +8,11 @@ godot := `echo "${GODOT:-godot} --rendering-driver ${RENDERING_DRIVER:-vulkan}"`
build:
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:
{{godot}} --headless -s plug.gd install

View file

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

View file

@ -10,7 +10,7 @@
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).
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>
<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:
- [**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.

View file

@ -48,6 +48,6 @@ Also feel free to open a new discussion in the [discussions](https://github.com/
## 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":
(
preload("../web_console/web_console.tscn")
if OS.has_feature("JavaScript")
if OS.has_feature("web")
else preload("../terminal/terminal.tscn")
)
},
@ -157,18 +157,16 @@ func _on_Terminal_key_pressed(data: String, event: InputEventKey) -> void:
)
return
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)
scene.grab_focus()
await pty.exited
$Terminal.grab_focus()
scene.queue_free()
"Exit":
pass
# FIXME
#if OS.has_feature("JavaScript"):
#JavaScript.eval("window.history.back() || window.close()")
#get_tree().quit()
if OS.has_feature("web"):
JavaScriptBridge.eval("window.history.back() || window.close()")
get_tree().quit()
func _on_Asciicast_key_pressed(

View file

@ -8,7 +8,7 @@ var line := ""
var _tput
@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):
@ -42,10 +42,10 @@ func _on_Terminal_key_pressed(_data, event: InputEventKey):
if not event:
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)
match event.scancode:
match event.keycode:
KEY_ENTER:
terminal.write("\r\n")
@ -53,22 +53,20 @@ func _on_Terminal_key_pressed(_data, event: InputEventKey):
return emit_signal("exited", 0)
if not _has_js:
var msg := "WebConsole only available in HTML5 build."
var msg := "WebConsole only available in Web build."
push_error(msg)
_tput.setaf(TPut.ANSIColor.red)
terminal.write(msg)
_tput.sgr0()
prompt("\r\n>> ")
else:
# TODO: godot4
pass
# var json = JavaScript.eval("JSON.stringify(%s)" % line, true)
# _tput.setaf(TPut.ANSIColor.magenta)
# terminal.write(str(json))
# _tput.sgr0()
var json = JavaScriptBridge.eval("JSON.stringify(%s)" % line, true)
_tput.setaf(TPut.ANSIColor.magenta)
terminal.write(str(json))
_tput.sgr0()
line = ""
#_tput.srg0()
_tput.sgr0()
prompt("\r\n>> ")
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"]
[node name="WebConsole" type="Control"]
@ -9,9 +10,12 @@ anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_28w8r")
script = ExtResource("3")
[node name="Terminal" type="Terminal" parent="."]
layout_mode = 0
anchor_right = 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]
name="HTML5"
platform="HTML5"
name="Web"
platform="Web"
runnable=true
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter="*.cast, addons/godot_xterm/plugin.cfg, addons/godot_xterm/themes/fonts/hack/hack_regular-3.003.ttf"
exclude_filter="misc/*, **/thirdparty/*, **/unifont/*, **/nerd_fonts/*, **/noto_color_emoji/*, *bold*.ttf, *italic*.ttf"
export_filter="resources"
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")
include_filter="addons/godot_xterm/native/bin/*.wasm"
exclude_filter=""
export_path="docs/demo/index.html"
script_export_mode=1
script_encryption_key=""
encryption_include_filters=""
encryption_exclude_filters=""
encrypt_pck=false
encrypt_directory=false
[preset.0.options]
custom_template/debug=""
custom_template/release=""
variant/export_type=2
variant/extensions_support=true
vram_texture_compression/for_desktop=true
vram_texture_compression/for_mobile=false
html/export_icon=true
@ -31,4 +35,4 @@ progressive_web_app/orientation=0
progressive_web_app/icon_144x144=""
progressive_web_app/icon_180x180=""
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);
});
})();