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

Testing Fresh Components #427

Open
TheMagicNacho opened this issue Jul 5, 2022 · 15 comments
Open

Testing Fresh Components #427

TheMagicNacho opened this issue Jul 5, 2022 · 15 comments

Comments

@TheMagicNacho
Copy link

Issue: Deno testing suit documentation does not elaborate on how to render and test components.

Discussion: I have been having trouble finding ways to conduct unit tests for Fresh components. When using react-testing-library, we can use render(<MyComponent newProp={'myProp') />) to render the component and test. However, this feature is missing with the current testing suit on Deno.

To "hack" a solution together I had tried using dom-testing-library and I had tried using preact render, however neither of these solutions allowed me to test if the component is working the way I expect it to.
I had also tried importing react-testing-libaray as a "hail Mary".

I understand Cypress is looking into how to perform integrated tests on Deno right now, however, finding a way to implement unit tests in fresh will allow us to improve TDD within this framework.

Recommendation: Documentation should be upgraded which describes how to render a component for testing. I do not think we need a new API or a new library to accomplish said means. That said, I do not have a solution for this problem yet.

@audrow
Copy link

audrow commented Jul 13, 2022

I haven't yet tried this, but maybe the answer is to use the testing that comes with Preact.
https://preactjs.com/guide/v10/preact-testing-library

@TheMagicNacho
Copy link
Author

TheMagicNacho commented Jul 24, 2022

Bottom Line Up Front: I found a solution by rendering the components through preact-render-to-string and using Demo DOM.

Prelude

Unfortunately, preact-testing-library does not work with Deno and fresh because preact-testing-library needs to be called within a DOM environment.

To overcome this limitation, I tried to update the Deno.json's compiler options to include "dom" per the Deno manual for jsdom but that didn't fix the problem.

Solution

At the top of the test file, we need to reference dom, deno.ns, and esnext.

We can render the Preact component using react to string then pass the string into the Deno DOMParser. From there, we can query the components we want to test then assert the results.

/** @jsx h */   
/// <reference no-default-lib="true"/>
/// <reference lib="dom" /> 
/// <reference lib="deno.ns" />
/// <reference lib="esnext" /> 

import { h } from 'preact';

import render from "https://esm.sh/[email protected]";
import { DOMParser } from "https://deno.land/x/[email protected]/deno-dom-wasm.ts";
import { describe, it } from "https://deno.land/[email protected]/testing/bdd.ts";
import { assertEquals, assertExists } from "https://deno.land/[email protected]/testing/asserts.ts";

import Card from '../components/Card.tsx';

describe('Card component', () => {
  it('should exists.', () => {
    const compAsString  = render(<Card contentTitle={'Test Title'} contentBody={'Lorem ispum...'} />)
    const doc = new DOMParser().parseFromString(compAsString, 'text/html');
  
    assertExists(doc);
  });

  it('should have a title and body.', () => {
    const compAsString  = render(<Card contentTitle={'Test Title'} contentBody={'Lorem ispum...'} />)
    const doc = new DOMParser().parseFromString(compAsString, 'text/html');

    const title = doc?.querySelector('h1');
    const body = doc?.querySelector('div');

    assertEquals(title?.textContent, 'Test Title')
    assertEquals(body?.textContent, 'Lorem ispum...')
  });
});

@trungthecelestial
Copy link

Any update on this issue?

@digitaldesigndj
Copy link
Contributor

This example is great, but when I render a component that uses asset() I get a lot of errors in the output:

Failed to create asset() URL, falling back to regular path ('/svg/andbounds-logo-white.svg'): ReferenceError: __FRSH_BUILD_ID is not defined

Obviously there's a better way to do this.

@MoaathAlattas
Copy link

MoaathAlattas commented Sep 18, 2022

@TheMagicNacho I was able to use preact-testing-library by defining a global document as mentioned here.

import { DOMParser } from "https://esm.sh/[email protected]"
import { render } from "https://esm.sh/@testing-library/[email protected][email protected]"; // make sure to specify preact version
import {describe, beforeEach, it} from "https://deno.land/[email protected]/testing/bdd.ts";
import { Nav } from "./nav.tsx";

describe("components/nav", () => {
  beforeEach(() => {
      window.document = new DOMParser().parseFromString("", "text/html") as any;
  });

  it("test example", () => {
    const { container } = render(<Nav />);
    assert(container.innerHTML.includes("Login"));
  });
})

@sigmaSd
Copy link
Contributor

sigmaSd commented Sep 18, 2022

Is it possible to run the examples here https://preactjs.com/guide/v10/preact-testing-library/ using this trick? I tested a bit screen api seems to fail. and also I had to use npm jsdom because deno dom is missing style support

@MoaathAlattas
Copy link

MoaathAlattas commented Sep 19, 2022

@sigmaSd could you show an example of how you are using JSDOM? Whenever i use JSDOM, the test runner never ends the process so I am using LinkeDOM for now.

Instead of using screen, same methods are available out of the render method:

const { getByText } = render(<Counter initialCount={5}/>);

It seems like screen doesn't recognize the global document.body and haven't dug deeper into this yet.
https://github.com/testing-library/dom-testing-library/blob/main/src/screen.ts

export const screen =
  typeof document !== 'undefined' && document.body // eslint-disable-line @typescript-eslint/no-unnecessary-condition
    ? getQueriesForElement(document.body, queries, initialValue)
    : Object.keys(queries).reduce((helpers, key) => {
        // `key` is for all intents and purposes the type of keyof `helpers`, which itself is the type of `initialValue` plus incoming properties from `queries`
        // if `Object.keys(something)` returned Array<keyof typeof something> this explicit type assertion would not be necessary
        // see https://stackoverflow.com/questions/55012174/why-doesnt-object-keys-return-a-keyof-type-in-typescript
        helpers[key as keyof typeof initialValue] = () => {
          throw new TypeError(
            'For queries bound to document.body a global document has to be available... Learn more: https://testing-library.com/s/screen-global-error',
          )
        }
        return helpers
      }, initialValue)

@sigmaSd
Copy link
Contributor

sigmaSd commented Sep 20, 2022

for jsdom I imported it with npm:jsdom and I had to patch vm.isContext to return false (denoland/deno#18315)

@Industrial
Copy link

@sigmaSd Did you get tests to run? I Can't get it to work at all.

testing-library/react-testing-library#669

@sigmaSd
Copy link
Contributor

sigmaSd commented Oct 4, 2022

Nope I got the same errors

@christian-bromann
Copy link

for jsdom I imported it with npm:jsdom and I had to patch vm.isContext to return false (denoland/deno#18315)

@sigmaSd how did you patch it?

@sigmaSd
Copy link
Contributor

sigmaSd commented Oct 13, 2022

I did it in a hacky way but I saw Industrial has a better method

https://discord.com/channels/684898665143206084/1022163295895027722/threads/1025572387695108187

image

@christian-bromann
Copy link

Yup, ended up doing the same, the only problem with JSDom is that it tries to access canvas primitives when trying to render images. I ended up having to replace image tags with div tags.

@radicand
Copy link

radicand commented Apr 7, 2023

For those using the method suggested by @MoaathAlattas , note that LinkeDOM and deno-dom do not accept empty strings for DOMParser.parseFromString(). I ran into weird errors until I came across WebReflection/linkedom#157 and fixed it by just initializing it with something.

export const initEnv = () => {
    globalThis.document = new DOMParser().parseFromString(
        "<html></html>", // this the main change
        "text/html"
    ) as unknown as Document;
    window.document = globalThis.document;
};

@Mrashes
Copy link
Contributor

Mrashes commented Jul 2, 2024

Coming to this a bit late but is there an approved way of doing this?

It would be great to have examples of how to test components in Fresh 2.0 docs. Working on my first fresh project and feeling the burn of not being able to test my components easily.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants