Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: improve wasm development, e2e test #1323

Merged
merged 2 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install .
echo "MARIMO_VERSION=$(marimo --version)" >> $GITHUB_ENV

- name: 🎭 Get installed Playwright version
id: playwright-version
Expand All @@ -93,6 +94,8 @@ jobs:
- name: 🎭 Run Playwright tests
working-directory: ./frontend
run: npx playwright test
env:
VITE_MARIMO_VERSION: ${{ env.MARIMO_VERSION }}

- name: ☁️ Google Auth
uses: google-github-actions/auth@v1
Expand Down
8 changes: 5 additions & 3 deletions frontend/e2e-tests/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,11 @@ export async function exportAsHTMLAndTakeScreenshot(page: Page) {
await takeScreenshot(exportPage, path);

// Toggle code
await exportPage.getByTestId("show-code").click();
// wait 100ms for the code to be shown
await exportPage.waitForTimeout(100);
if (await exportPage.isVisible("[data-testid=show-code]")) {
await exportPage.getByTestId("show-code").click();
// wait 100ms for the code to be shown
await exportPage.waitForTimeout(100);
}

// Take screenshot of code
await takeScreenshot(exportPage, `code-${path}`);
Expand Down
34 changes: 34 additions & 0 deletions frontend/e2e-tests/kitchen-sink-wasm.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* Copyright 2024 Marimo. All rights reserved. */
import { test } from "@playwright/test";
import {
exportAsHTMLAndTakeScreenshot,
exportAsPNG,
takeScreenshot,
} from "./helper";
import { fileURLToPath } from "node:url";

const __filename = fileURLToPath(import.meta.url);

test("can screenshot and download as html edit", async ({ page }) => {
await page.goto("http://localhost:3000");

// See text Initializing
await page.waitForSelector("text=Initializing");
// See text Welcome
await page.waitForSelector("text=Welcome");

await takeScreenshot(page, __filename);
await exportAsHTMLAndTakeScreenshot(page);
});

test("can screenshot and download as html in run", async ({ page }) => {
await page.goto("http://localhost:3000?mode=read");

// See text Initializing
await page.waitForSelector("text=Initializing");
// See text Welcome
await page.waitForSelector("text=Welcome");

await takeScreenshot(page, __filename);
await exportAsHTMLAndTakeScreenshot(page);
});
7 changes: 7 additions & 0 deletions frontend/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ export function startServer(app: ApplicationNames): void {
exec(marimoCmd);
}

const WASM_SERVER = {
command: `PYODIDE=true vite --port 3000`,
url: "http://localhost:3000",
reuseExistingServer: !!process.env.CI,
};

// See https://playwright.dev/docs/test-configuration.
const config: PlaywrightTestConfig = {
testDir: "./e2e-tests",
Expand Down Expand Up @@ -172,6 +178,7 @@ const config: PlaywrightTestConfig = {
url: getUrl(EDIT_PORT),
reuseExistingServer: false,
},
WASM_SERVER,
],
};

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/core/pyodide/worker/getMarimoWheel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export function getMarimoWheel(version: string) {
return "marimo >= 0.3.0";
}
if (version === "local") {
return "http://localhost:8000/dist/marimo-0.4.4-py3-none-any.whl";
return `http://localhost:8000/dist/marimo-${import.meta.env.VITE_MARIMO_VERSION}-py3-none-any.whl`;
}
if (version === "latest") {
return "marimo";
Expand Down
18 changes: 16 additions & 2 deletions frontend/vite.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,23 @@ const htmlDevPlugin = (): Plugin => {
}

if (isPyodide) {
const modeFromUrl = ctx.originalUrl?.includes("mode=read")
? "read"
: "edit";
html = html.replace("{{ base_url }}", "");
html = html.replace("{{ title }}", "marimo");
html = html.replace("{{ user_config }}", JSON.stringify({}));
html = html.replace("{{ app_config }}", JSON.stringify({}));
html = html.replace("{{ server_token }}", "");
html = html.replace("{{ version }}", "local");
if (process.env.VITE_MARIMO_VERSION) {
// If VITE_MARIMO_VERSION is defined, pull the local version of marimo
html = html.replace("{{ version }}", "local");
} else {
// Otherwise, pull the latest version of marimo from PyPI
html = html.replace("{{ version }}", "latest");
}
html = html.replace("{{ filename }}", "notebook.py");
html = html.replace("{{ mode }}", "edit");
html = html.replace("{{ mode }}", modeFromUrl);
html = html.replace(/<\/head>/, `<marimo-wasm></marimo-wasm></head>`);
return html;
}
Expand Down Expand Up @@ -105,6 +114,11 @@ export default defineConfig({
"Cross-Origin-Embedder-Policy": "require-corp",
},
},
define: {
"import.meta.env.VITE_MARIMO_VERSION": process.env.VITE_MARIMO_VERSION
? JSON.stringify(process.env.VITE_MARIMO_VERSION)
: JSON.stringify("latest"),
},
build: {
minify: isDev ? false : "terser",
sourcemap: isDev,
Expand Down
7 changes: 5 additions & 2 deletions marimo/_config/manager.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright 2024 Marimo. All rights reserved.
import os
from __future__ import annotations

import tomlkit
import os

from marimo import _loggers
from marimo._config.config import (
Expand All @@ -21,12 +21,15 @@ def __init__(self) -> None:
self.config = load_config()

def save_config(self, config: MarimoConfig) -> MarimoConfig:
import tomlkit

config_path = self._get_config_path()
LOGGER.debug("Saving user configuration to %s", config_path)
# Remove the secret placeholders from the incoming config
config = remove_secret_placeholders(config)
# Merge the current config with the new config
merged = merge_config(self.config, config)

with open(config_path, "w", encoding="utf-8") as f:
tomlkit.dump(merged, f)

Expand Down
6 changes: 4 additions & 2 deletions marimo/_config/utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Copyright 2024 Marimo. All rights reserved.
from __future__ import annotations

import os
from typing import Optional, cast

import tomlkit

from marimo import _loggers
from marimo._config.config import (
DEFAULT_CONFIG,
Expand Down Expand Up @@ -98,6 +98,8 @@ def load_config() -> MarimoConfig:
if path is not None:
LOGGER.debug("Using config at %s", path)
try:
import tomlkit

with open(path, "rb") as f:
user_config = tomlkit.parse(f.read())
except Exception as e:
Expand Down
16 changes: 6 additions & 10 deletions marimo/_islands/island_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import json
from textwrap import dedent
from typing import TYPE_CHECKING, List, Optional, Union, cast
from typing import List, Optional, Union, cast

from marimo import __version__, _loggers
from marimo._ast.app import App, InternalApp
Expand All @@ -14,9 +14,7 @@
from marimo._output.utils import uri_encode_component
from marimo._plugins.stateless.json_output import json_output
from marimo._plugins.ui import code_editor

if TYPE_CHECKING:
from marimo._server.sessions import Session
from marimo._server.session.session_view import SessionView

LOGGER = _loggers.marimo_logger()

Expand All @@ -40,7 +38,7 @@ def __init__(
self._is_reactive = is_reactive

self._internal_app: Optional[InternalApp] = None
self._session: Optional[Session] = None
self._session_view: Optional[SessionView] = None
self._output: Optional[CellOutput] = None

@property
Expand All @@ -49,14 +47,12 @@ def output(self) -> Optional[CellOutput]:
# pdf.
if self._output is None:
assert (
self._session is not None
self._session_view is not None
), "You must call build() before rendering"
assert (
self._internal_app is not None
), "You must call build() accessing output"
outputs = self._session.session_view.get_cell_outputs(
[self._cell_id]
)
outputs = self._session_view.get_cell_outputs([self._cell_id])
self._output = outputs.get(self._cell_id, None)
return self._output

Expand Down Expand Up @@ -245,7 +241,7 @@ async def build(self) -> App:

for stub in self._stubs:
stub._internal_app = self._app
stub._session = session
stub._session_view = session

return cast(App, self._app)

Expand Down
14 changes: 8 additions & 6 deletions marimo/_server/export/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from marimo._server.model import ConnectionState, SessionConsumer, SessionMode
from marimo._server.models.export import ExportAsHTMLRequest
from marimo._server.models.models import InstantiateRequest
from marimo._server.sessions import Session
from marimo._server.session.session_view import SessionView


def export_as_script(
Expand Down Expand Up @@ -51,11 +51,11 @@ async def run_app_then_export_as_html(
file_manager = file_router.get_file_manager(file_key)

config = UserConfigManager()
session = await run_app_until_completion(file_manager, cli_args)
session_view = await run_app_until_completion(file_manager, cli_args)
# Export the session as HTML
html, filename = Exporter().export_as_html(
file_manager=session.app_file_manager,
session_view=session.session_view,
file_manager=file_manager,
session_view=session_view,
display_config=config.get_config()["display"],
request=ExportAsHTMLRequest(
include_code=include_code,
Expand All @@ -69,7 +69,9 @@ async def run_app_then_export_as_html(
async def run_app_until_completion(
file_manager: AppFileManager,
cli_args: SerializedCLIArgs,
) -> Session:
) -> SessionView:
from marimo._server.sessions import Session

instantiated_event = asyncio.Event()

# Create a no-op session consumer
Expand Down Expand Up @@ -130,4 +132,4 @@ def connection_state(self) -> ConnectionState:
# Stop distributor, terminate kernel process, etc -- all information is
# captured by the session view.
session.close()
return session
return session.session_view
20 changes: 7 additions & 13 deletions marimo/_server/export/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
from typing import cast

from marimo import __version__
from marimo._cli.convert.markdown import (
formatted_code_block,
is_sanitized_markdown,
)
from marimo._config.config import (
DEFAULT_CONFIG,
DisplayConfig,
Expand All @@ -32,15 +28,6 @@
from marimo._server.templates.templates import static_notebook_template
from marimo._utils.paths import import_files

# Click not bound to be installed (e.g. pyodide).
try:
from click import UsageError
except ImportError:

class UsageError(Exception): # type: ignore[no-redef]
pass
Comment on lines -35 to -41
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not since we lazily import



# Root directory for static assets
root = os.path.realpath(str(import_files("marimo").joinpath("_static")))

Expand Down Expand Up @@ -116,6 +103,8 @@ def export_as_script(
if not cell:
continue
if cell._is_coroutine():
from click import UsageError

raise UsageError(
"Cannot export a notebook with async code to a flat script"
)
Expand All @@ -134,6 +123,11 @@ def export_as_script(
return code, download_filename

def export_as_md(self, file_manager: AppFileManager) -> tuple[str, str]:
from marimo._cli.convert.markdown import (
formatted_code_block,
is_sanitized_markdown,
)

# TODO: Provide filter or kernel in header such that markdown documents
# are executable.
document = [
Expand Down
18 changes: 16 additions & 2 deletions pyodide/DOCS.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
# marimo + pyodide

## Running the frontend against the latest deploy on PyPi

```bash
python3 pyodide/build_and_serve.py
# then in another terminal
cd frontend
PYODIDE=true pnpm dev
```

## Running the frontend against a local backend

This requires `watchdog` to be installed (`pip install watchdog`).

```bash
# build once
python3 -m build
# server and watch for changes
python3 pyodide/build_and_serve.py
# in another terminal
cd frontend
PYODIDE=true VITE_MARIMO_VERSION=$(marimo --version) pnpm dev
```
Loading