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

Scoped Live Testing #52

Open
DrSensor opened this issue Jan 14, 2023 · 1 comment
Open

Scoped Live Testing #52

DrSensor opened this issue Jan 14, 2023 · 1 comment
Labels
documentation Improvements or additions to documentation enhancement New feature or request

Comments

@DrSensor
Copy link
Owner

DrSensor commented Jan 14, 2023

Run test suite on every live reload only on scope that is in the viewport. There's 2 approach based on how test suites got removed on the production code:

  1. Either inline or import test suite in the linked module. This approach rely on tree shaking mechanism in JS bundler.
import { current, scope, use } from "nusa/std"

export default class Counter {
  accessor count
  increment() {
    this.count += +current.target.value
  }
}

import { suite, test } from "@testdeck/jasmine"
import { expect } from "chai"

if (__TEST__)
  @suite class TestCounter {
    counter = use(Counter)
    @test count_when_clicked() {
      scope(this.counter)
        .getElementByTagName("button")[0]
        .click()

      expect(this.counter.count).toBe(1);
    }
  }

or

import { current } from "nusa/std"

export default class Counter {
  accessor count
  increment() {
    this.count += +current.target.value
  }
}

if (__TEST__) await import("./counter.test.js")
import { suite, test } from "@testdeck/jasmine"
import { expect } from "chai"
import { scope, use } from "nusa/std"

import Counter from "./counter.js"

@suite class TestCounter {
  counter = use(Counter)
  @test count_when_clicked() {
    scope(this.counter)
      .getElementByTagName("button")[0]
      .click()

    expect(this.counter.count).toBe(1);
  }
}

export default TestCounter // for <link>-ing (optional)
  1. <link> test module. This rely on site generator to remove specific elements based on specific attribute.
<render-scope>
  <link href=./counter.js>
  <link href=./counter.test.js>

  <template shadowroot=closed>
    <button :: on:click=increment>++</button>
    <span :: text:=count>0</span>
  </template>
</render-scope>

then site generator (i.e lume) can remove the testing code like

if (!DEV) site.process([".html"], ({ document }) => {
  document
    .querySelectorAll('link[href$=".test.js"]')
    .forEach(element => element.remove())
})

Food for Thought

To make it toolless, use BroadcastChannel to communicate between dev, report, and test environment. For example:

  • localhost:3000 - dev environment. No test are running (__TEST__ === false). Trigger/send test signal via BroadcastChannel or WebSocket when <render-scope> intersect with viewport
  • localhost:??? - test environment. It might run in:
    • headless browser via WebSocket (run in different port i.e localhost:5000)
    • or just new tab or popup without spawning new browser instance (communicate via BroadcastChannel, run in same port i.e localhost:3000)
  • localhost:3000/.well-known/test-report or inside debug-panel #44 - view and control how test being run
@DrSensor DrSensor added documentation Improvements or additions to documentation enhancement New feature or request labels Jan 14, 2023
@DrSensor
Copy link
Owner Author

DrSensor commented Mar 2, 2023

I've decided to use zora for testing both demo and internal implementation and expose a <button>test</button> to manually run the tests. Since use() and scope() are context specific, it must be used inside callback like @build decorator or others. So I plan to expose a lifecycle module specifically (but not limited) for testing.

import * as lifecycle from "//esm.run/nusa/lifecycle"
import { current, use } from "//esm.run/nusa/std"
import { test } from "zora"
import Counter from "./module.js"

const testbed = lifecycle.render(() => {
  const counter = use(Counter)
  const button = current.root.getElementsByTagName("button")[0]

  test(name, t => {
    button.click()
    t.equal(counter.count, 1);
  })

  lifecycle.clear(testbed)
})

const name = import.meta.url.split("/").at(-2)
<script type=module src=test.js></script>
<script async src=//esm.run/nusa/render-scope></script>

<render-scope>
  <link href=module.js>

  <template shadowrootmode=closed>
    <button :: on:click=increment text:=count/>
  </template>
</render-scope>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant