diff --git a/leptos/src/suspense.rs b/leptos/src/suspense.rs
index f23764650..70331e8b9 100644
--- a/leptos/src/suspense.rs
+++ b/leptos/src/suspense.rs
@@ -79,9 +79,9 @@ where
cfg_if! {
if #[cfg(any(feature = "csr", feature = "hydrate"))] {
if context.ready() {
- orig_child(cx).into_view(cx)
+ Fragment::lazy(Box::new(|| vec![orig_child(cx).into_view(cx)])).into_view(cx)
} else {
- fallback().into_view(cx)
+ Fragment::lazy(Box::new(|| vec![fallback().into_view(cx)])).into_view(cx)
}
} else {
use leptos_reactive::signal_prelude::*;
@@ -108,10 +108,12 @@ where
let orig_child = Rc::clone(&orig_child);
move || {
HydrationCtx::continue_from(current_id.clone());
- DynChild::new(move || orig_child(cx))
- .into_view(cx)
- .render_to_string(cx)
- .to_string()
+ Fragment::lazy(Box::new(move || {
+ vec![DynChild::new(move || orig_child(cx)).into_view(cx)]
+ }))
+ .into_view(cx)
+ .render_to_string(cx)
+ .to_string()
}
},
// in-order streaming
@@ -119,11 +121,13 @@ where
let current_id = current_id.clone();
move || {
HydrationCtx::continue_from(current_id.clone());
- DynChild::new(move || orig_child(cx))
- .into_view(cx)
- .into_stream_chunks(cx)
+ Fragment::lazy(Box::new(move || {
+ vec![DynChild::new(move || orig_child(cx)).into_view(cx)]
+ }))
+ .into_view(cx)
+ .into_stream_chunks(cx)
}
- }
+ },
);
// return the fallback for now, wrapped in fragment identifier
diff --git a/leptos/tests/test_examples/suspense-tests/.gitignore b/leptos/tests/test_examples/suspense-tests/.gitignore
new file mode 100644
index 000000000..8cdaa33de
--- /dev/null
+++ b/leptos/tests/test_examples/suspense-tests/.gitignore
@@ -0,0 +1,13 @@
+# Generated by Cargo
+# will have compiled files and executables
+/target/
+pkg
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
+
+# node e2e test tools and outputs
+node_modules/
+test-results/
+end2end/playwright-report/
+playwright/.cache/
diff --git a/leptos/tests/test_examples/suspense-tests/Cargo.toml b/leptos/tests/test_examples/suspense-tests/Cargo.toml
new file mode 100644
index 000000000..4f06aacda
--- /dev/null
+++ b/leptos/tests/test_examples/suspense-tests/Cargo.toml
@@ -0,0 +1,78 @@
+[package]
+name = "leptos_start"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "rlib"]
+
+[dependencies]
+actix-files = { version = "0.6", optional = true }
+actix-web = { version = "4", optional = true, features = ["macros"] }
+console_error_panic_hook = "0.1"
+console_log = "1"
+cfg-if = "1"
+leptos = { path = "../../..", default-features = false, features = ["serde"] }
+leptos_actix = { path = "../../../../integrations/actix", optional = true }
+leptos_router = { path = "../../../../router", default-features = false }
+log = "0.4"
+simple_logger = "4"
+wasm-bindgen = "0.2"
+serde = "1.0.159"
+tokio = { version = "1.27.0", features = ["time"], optional = true }
+
+[features]
+hydrate = ["leptos/hydrate", "leptos_router/hydrate"]
+ssr = [
+ "dep:actix-files",
+ "dep:actix-web",
+ "dep:leptos_actix",
+ "leptos/ssr",
+ "leptos_router/ssr",
+ "dep:tokio",
+]
+
+[package.metadata.leptos]
+# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
+output-name = "leptos_start"
+# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.
+site-root = "target/site"
+# The site-root relative folder where all compiled output (JS, WASM and CSS) is written
+# Defaults to pkg
+site-pkg-dir = "pkg"
+# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
+site-addr = "127.0.0.1:3000"
+# The port to use for automatic reload monitoring
+reload-port = 3001
+# [Optional] Command to use when running end2end tests. It will run in the end2end dir.
+# [Windows] for non-WSL use "npx.cmd playwright test"
+# This binary name can be checked in Powershell with Get-Command npx
+end2end-cmd = "npx playwright test"
+end2end-dir = "end2end"
+# The browserlist query used for optimizing the CSS.
+browserquery = "defaults"
+# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
+watch = false
+# The environment Leptos will run in, usually either "DEV" or "PROD"
+env = "DEV"
+# The features to use when compiling the bin target
+#
+# Optional. Can be over-ridden with the command line parameter --bin-features
+bin-features = ["ssr"]
+
+# If the --no-default-features flag should be used when compiling the bin target
+#
+# Optional. Defaults to false.
+bin-default-features = false
+
+# The features to use when compiling the lib target
+#
+# Optional. Can be over-ridden with the command line parameter --lib-features
+lib-features = ["hydrate"]
+
+# If the --no-default-features flag should be used when compiling the lib target
+#
+# Optional. Defaults to false.
+lib-default-features = false
+
+[workspace]
diff --git a/leptos/tests/test_examples/suspense-tests/LICENSE b/leptos/tests/test_examples/suspense-tests/LICENSE
new file mode 100644
index 000000000..e869ce3b4
--- /dev/null
+++ b/leptos/tests/test_examples/suspense-tests/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 henrik
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/leptos/tests/test_examples/suspense-tests/README.md b/leptos/tests/test_examples/suspense-tests/README.md
new file mode 100644
index 000000000..426f5a594
--- /dev/null
+++ b/leptos/tests/test_examples/suspense-tests/README.md
@@ -0,0 +1,61 @@
+
+
+# Leptos Starter Template
+
+This is a template for use with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool.
+
+## Creating your template repo
+
+If you don't have `cargo-leptos` installed you can install it with
+
+`cargo install cargo-leptos`
+
+Then run
+
+`cargo leptos new --git leptos-rs/start`
+
+to generate a new project template.
+
+`cd {projectname}`
+
+to go to your newly created project.
+
+Of course you should explore around the project structure, but the best place to start with your application code is in `src/app.rs`.
+
+## Running your project
+
+`cargo leptos watch`
+
+## Installing Additional Tools
+
+By default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.
+
+1. `rustup toolchain install nightly --allow-downgrade` - make sure you have Rust nightly
+2. `rustup default nightly` - setup nightly as default, or you can use rust-toolchain file later on
+3. `rustup target add wasm32-unknown-unknown` - add the ability to compile Rust to WebAssembly
+4. `cargo install cargo-generate` - install `cargo-generate` binary (should be installed automatically in future)
+5. `npm install -g sass` - install `dart-sass` (should be optional in future)
+
+## Executing a Server on a Remote Machine Without the Toolchain
+After running a `cargo leptos build --release` the minimum files needed are:
+
+1. The server binary located in `target/server/release`
+2. The `site` directory and all files within located in `target/site`
+
+Copy these files to your remote server. The directory structure should be:
+```text
+leptos_start
+site/
+```
+Set the following enviornment variables (updating for your project as needed):
+```text
+LEPTOS_OUTPUT_NAME="leptos_start"
+LEPTOS_SITE_ROOT="site"
+LEPTOS_SITE_PKG_DIR="pkg"
+LEPTOS_SITE_ADDR="127.0.0.1:3000"
+LEPTOS_RELOAD_PORT="3001"
+```
+Finally, run the server binary.
diff --git a/leptos/tests/test_examples/suspense-tests/end2end/package-lock.json b/leptos/tests/test_examples/suspense-tests/end2end/package-lock.json
new file mode 100644
index 000000000..f12af4425
--- /dev/null
+++ b/leptos/tests/test_examples/suspense-tests/end2end/package-lock.json
@@ -0,0 +1,74 @@
+{
+ "name": "end2end",
+ "version": "1.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "end2end",
+ "version": "1.0.0",
+ "license": "ISC",
+ "devDependencies": {
+ "@playwright/test": "^1.28.0"
+ }
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.28.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.28.0.tgz",
+ "integrity": "sha512-vrHs5DFTPwYox5SGKq/7TDn/S4q6RA1zArd7uhO6EyP9hj3XgZBBM12ktMbnDQNxh/fL1IUKsTNLxihmsU38lQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "playwright-core": "1.28.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "18.11.9",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
+ "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
+ "dev": true
+ },
+ "node_modules/playwright-core": {
+ "version": "1.28.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.28.0.tgz",
+ "integrity": "sha512-nJLknd28kPBiCNTbqpu6Wmkrh63OEqJSFw9xOfL9qxfNwody7h6/L3O2dZoWQ6Oxcm0VOHjWmGiCUGkc0X3VZA==",
+ "dev": true,
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ }
+ },
+ "dependencies": {
+ "@playwright/test": {
+ "version": "1.28.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.28.0.tgz",
+ "integrity": "sha512-vrHs5DFTPwYox5SGKq/7TDn/S4q6RA1zArd7uhO6EyP9hj3XgZBBM12ktMbnDQNxh/fL1IUKsTNLxihmsU38lQ==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "playwright-core": "1.28.0"
+ }
+ },
+ "@types/node": {
+ "version": "18.11.9",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
+ "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
+ "dev": true
+ },
+ "playwright-core": {
+ "version": "1.28.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.28.0.tgz",
+ "integrity": "sha512-nJLknd28kPBiCNTbqpu6Wmkrh63OEqJSFw9xOfL9qxfNwody7h6/L3O2dZoWQ6Oxcm0VOHjWmGiCUGkc0X3VZA==",
+ "dev": true
+ }
+ }
+}
diff --git a/leptos/tests/test_examples/suspense-tests/end2end/package.json b/leptos/tests/test_examples/suspense-tests/end2end/package.json
new file mode 100644
index 000000000..ed785859f
--- /dev/null
+++ b/leptos/tests/test_examples/suspense-tests/end2end/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "end2end",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {},
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "@playwright/test": "^1.28.0"
+ }
+}
diff --git a/leptos/tests/test_examples/suspense-tests/end2end/playwright.config.ts b/leptos/tests/test_examples/suspense-tests/end2end/playwright.config.ts
new file mode 100644
index 000000000..e9891c094
--- /dev/null
+++ b/leptos/tests/test_examples/suspense-tests/end2end/playwright.config.ts
@@ -0,0 +1,107 @@
+import type { PlaywrightTestConfig } from "@playwright/test";
+import { devices } from "@playwright/test";
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// require('dotenv').config();
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+const config: PlaywrightTestConfig = {
+ testDir: "./tests",
+ /* Maximum time one test can run for. */
+ timeout: 30 * 1000,
+ expect: {
+ /**
+ * Maximum time expect() should wait for the condition to be met.
+ * For example in `await expect(locator).toHaveText();`
+ */
+ timeout: 5000,
+ },
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: "html",
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
+ actionTimeout: 0,
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ // baseURL: 'http://localhost:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: "on-first-retry",
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: "chromium",
+ use: {
+ ...devices["Desktop Chrome"],
+ },
+ },
+
+ {
+ name: "firefox",
+ use: {
+ ...devices["Desktop Firefox"],
+ },
+ },
+
+ {
+ name: "webkit",
+ use: {
+ ...devices["Desktop Safari"],
+ },
+ },
+
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: {
+ // ...devices['Pixel 5'],
+ // },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: {
+ // ...devices['iPhone 12'],
+ // },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: {
+ // channel: 'msedge',
+ // },
+ // },
+ // {
+ // name: 'Google Chrome',
+ // use: {
+ // channel: 'chrome',
+ // },
+ // },
+ ],
+
+ /* Folder for test artifacts such as screenshots, videos, traces, etc. */
+ // outputDir: 'test-results/',
+
+ /* Run your local dev server before starting the tests */
+ // webServer: {
+ // command: 'npm run start',
+ // port: 3000,
+ // },
+};
+
+export default config;
diff --git a/leptos/tests/test_examples/suspense-tests/end2end/tests/example.spec.ts b/leptos/tests/test_examples/suspense-tests/end2end/tests/example.spec.ts
new file mode 100644
index 000000000..a461f351a
--- /dev/null
+++ b/leptos/tests/test_examples/suspense-tests/end2end/tests/example.spec.ts
@@ -0,0 +1,9 @@
+import { test, expect } from "@playwright/test";
+
+test("homepage has title and links to intro page", async ({ page }) => {
+ await page.goto("http://localhost:3000/");
+
+ await expect(page).toHaveTitle("Welcome to Leptos");
+
+ await expect(page.locator("h1")).toHaveText("Welcome to Leptos!");
+});
diff --git a/leptos/tests/test_examples/suspense-tests/src/app.rs b/leptos/tests/test_examples/suspense-tests/src/app.rs
new file mode 100644
index 000000000..04cd2254a
--- /dev/null
+++ b/leptos/tests/test_examples/suspense-tests/src/app.rs
@@ -0,0 +1,219 @@
+use leptos::*;
+use leptos_router::*;
+
+#[server(OneSecondFn "/api")]
+async fn one_second_fn(query: ()) -> Result<(), ServerFnError> {
+ tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
+
+ Ok(())
+}
+
+#[server(TwoSecondFn "/api")]
+async fn two_second_fn(query: ()) -> Result<(), ServerFnError> {
+ tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
+
+ Ok(())
+}
+
+#[component]
+pub fn App(cx: Scope) -> impl IntoView {
+ let style = r#"
+ nav {
+ display: flex;
+ width: 100%;
+ justify-content: space-around;
+ }
+
+ [aria-current] {
+ font-weight: bold;
+ }
+ "#;
+ view! {
+ cx,
+
+
+
+
+
+ }
+ />
+ // out-of-order
+
+