Skip to content

Commit d725b4e

Browse files
committed
Add typescript quickstart smoketest
1 parent b69e776 commit d725b4e

File tree

3 files changed

+51
-8
lines changed

3 files changed

+51
-8
lines changed

.github/workflows/ci.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ jobs:
5050
- uses: actions/setup-dotnet@v4
5151
with:
5252
global-json-file: global.json
53+
54+
# nodejs and pnpm are required for the typescript quickstart smoketest
55+
- name: Set up Node.js
56+
uses: actions/setup-node@v4
57+
with:
58+
node-version: 18
59+
60+
- uses: pnpm/action-setup@v4
61+
with:
62+
run_install: true
63+
5364
- name: Install psql (Windows)
5465
if: runner.os == 'Windows'
5566
run: choco install psql -y --no-progress

smoketests/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import logging
1515
import http.client
1616
import tomllib
17+
import functools
1718

1819
# miscellaneous file paths
1920
TEST_DIR = Path(__file__).parent
@@ -28,6 +29,7 @@
2829
TEMPLATE_CARGO_TOML = open(STDB_DIR / "crates/cli/src/subcommands/project/rust/Cargo._toml").read()
2930
bindings_path = (STDB_DIR / "crates/bindings").absolute()
3031
escaped_bindings_path = str(bindings_path).replace('\\', '\\\\\\\\') # double escape for re.sub + toml
32+
TYPESCRIPT_BINDINGS_PATH = (STDB_DIR / "crates/bindings-typescript").absolute()
3133
TEMPLATE_CARGO_TOML = (re.compile(r"^spacetimedb\s*=.*$", re.M) \
3234
.sub(f'spacetimedb = {{ path = "{escaped_bindings_path}", features = {{features}} }}', TEMPLATE_CARGO_TOML))
3335

@@ -170,6 +172,11 @@ def run_cmd(*args, capture_stderr=True, check=True, full_output=False, cmd_name=
170172
output.check_returncode()
171173
return output if full_output else output.stdout
172174

175+
@functools.cache
176+
def build_typescript_sdk():
177+
run_cmd("pnpm", "install", cwd=TYPESCRIPT_BINDINGS_PATH)
178+
run_cmd("pnpm", "build", cwd=TYPESCRIPT_BINDINGS_PATH)
179+
173180
def spacetime(*args, **kwargs):
174181
return run_cmd(SPACETIME_BIN, *args, cmd_name="spacetime", **kwargs)
175182

smoketests/tests/quickstart.py

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import xmltodict
77

88
import smoketests
9-
from .. import Smoketest, STDB_DIR, run_cmd, TEMPLATE_CARGO_TOML
9+
from .. import Smoketest, STDB_DIR, run_cmd, TEMPLATE_CARGO_TOML, TYPESCRIPT_BINDINGS_PATH, build_typescript_sdk
1010

1111

1212
def _write_file(path: Path, content: str):
@@ -19,12 +19,13 @@ def _append_to_file(path: Path, content: str):
1919
f.write(content)
2020

2121

22-
def _parse_quickstart(doc_path: Path, language: str) -> str:
22+
def _parse_quickstart(doc_path: Path, language: str, module_name: str) -> str:
2323
"""Extract code blocks from `quickstart.md` docs.
2424
This will replicate the steps in the quickstart guide, so if it fails the quickstart guide is broken.
2525
"""
2626
content = Path(doc_path).read_text()
27-
blocks = re.findall(rf"```{language}\n(.*?)\n```", content, re.DOTALL)
27+
codeblock_lang = "ts" if language == "typescript" else language
28+
blocks = re.findall(rf"```{codeblock_lang}\n(.*?)\n```", content, re.DOTALL)
2829

2930
end = ""
3031
if language == "csharp":
@@ -42,7 +43,7 @@ def _parse_quickstart(doc_path: Path, language: str) -> str:
4243
filtered_blocks.append(block)
4344
blocks = filtered_blocks
4445
# So we could have a different db for each language
45-
return "\n".join(blocks).replace("quickstart-chat", f"quickstart-chat-{language}") + end
46+
return "\n".join(blocks).replace("quickstart-chat", module_name) + end
4647

4748
def load_nuget_config(p: Path):
4849
if p.exists():
@@ -101,6 +102,8 @@ class BaseQuickstart(Smoketest):
101102
MODULE_CODE = ""
102103

103104
lang = None
105+
client_lang = None
106+
codeblock_langs = None
104107
server_doc = None
105108
client_doc = None
106109
server_file = None
@@ -118,13 +121,17 @@ def project_init(self, path: Path):
118121
def sdk_setup(self, path: Path):
119122
raise NotImplementedError
120123

124+
@property
125+
def _module_name(self):
126+
return f"quickstart-chat-{self.lang}"
127+
121128
def _publish(self) -> Path:
122129
base_path = Path(self.enterClassContext(tempfile.TemporaryDirectory()))
123130
server_path = base_path / "server"
124131
self.project_path = server_path
125132

126133
self.generate_server(server_path)
127-
self.publish_module(f"quickstart-chat-{self.lang}", capture_stderr=True, clear=True)
134+
self.publish_module(self._module_name, capture_stderr=True, clear=True)
128135
return base_path / "client"
129136

130137
def generate_server(self, server_path: Path):
@@ -133,7 +140,7 @@ def generate_server(self, server_path: Path):
133140
self.spacetime("init", "--lang", self.lang, server_path, capture_stderr=True)
134141
shutil.copy2(STDB_DIR / "rust-toolchain.toml", server_path)
135142
# Replay the quickstart guide steps
136-
_write_file(server_path / self.server_file, _parse_quickstart(self.server_doc, self.lang))
143+
_write_file(server_path / self.server_file, _parse_quickstart(self.server_doc, self.lang, self._module_name))
137144
self.server_postprocess(server_path)
138145
self.spacetime("build", "-d", "-p", server_path, capture_stderr=True)
139146

@@ -155,13 +162,14 @@ def _test_quickstart(self):
155162

156163
run_cmd(*self.build_cmd, cwd=client_path, capture_stderr=True)
157164

165+
client_lang = self.client_lang or self.lang
158166
self.spacetime(
159-
"generate", "--lang", self.lang,
167+
"generate", "--lang", client_lang,
160168
"--out-dir", client_path / self.module_bindings,
161169
"--project-path", self.project_path, capture_stderr=True
162170
)
163171
# Replay the quickstart guide steps
164-
main = _parse_quickstart(self.client_doc, self.lang)
172+
main = _parse_quickstart(self.client_doc, client_lang, self._module_name)
165173
for src, dst in self.replacements.items():
166174
main = main.replace(src, dst)
167175
main += "\n" + self.extra_code
@@ -314,3 +322,20 @@ def test_quickstart(self):
314322
if not smoketests.HAVE_DOTNET:
315323
self.skipTest("C# SDK requires .NET to be installed.")
316324
self._test_quickstart()
325+
326+
# We use the Rust client for testing the TypeScript server quickstart because
327+
# the TypeScript client quickstart is a React app, which is difficult to
328+
# smoketest.
329+
class TypeScript(Rust):
330+
lang = "typescript"
331+
client_lang = "rust"
332+
server_doc = STDB_DIR / "docs/docs/06-Server Module Languages/05-typescript-quickstart.md"
333+
server_file = "src/index.ts"
334+
335+
def server_postprocess(self, server_path: Path):
336+
build_typescript_sdk()
337+
run_cmd("pnpm", "install", f"spacetimedb@file:{TYPESCRIPT_BINDINGS_PATH}", cwd=server_path)
338+
339+
def test_quickstart(self):
340+
"""Run the TypeScript quickstart guides for server."""
341+
self._test_quickstart()

0 commit comments

Comments
 (0)