mzgoddard / preact-render-spy Goto Github PK
View Code? Open in Web Editor NEWRender preact components with access to the produced virtual dom for testing.
Home Page: https://www.npmjs.com/package/preact-render-spy
License: ISC License
Render preact components with access to the produced virtual dom for testing.
Home Page: https://www.npmjs.com/package/preact-render-spy
License: ISC License
Can you add documentation for running on node?
I am currently using a hack found in a closed issue:
import 'undom/register';
global['document'].createDocumentFragment = () => global['document'].createElement('#fragment');
Hopefully there's a way to do this without messing with global objects!
Implement FindWrapper#name
function that returns name of the current node.
Add a deprecate message and version deadline for removing childAt
. It does not add enough user value for the (albeit small) added maintenance burden.
Firstly, thank you to all the maintainers and contributors who have worked on this - this package makes testing Preact apps so much easier!
I was just wondering if there's there any reason why a component can't be used as a selector for the render context's find
method? (find(Selector)
or find(ImportedComponent)
, for example.)
The documentation and types added in #78 only cover nodes, but as far as I can tell this already works: selToWhere(Selector)
returns { nodeName: Selector }
, and isWhere
already handles nodeName
values that are functions.
This would make testing in TypeScript easier, as creating a JSX selector without the required props causes type errors, while including the required props can make the test overly specific.
Happy to open a PR to add tests and update the types if that's all that's required.
Lots of the preact community seems to be using TypeScript, though I'm unfamiliar with how to do it myself, it would be awesome if we could create the type defs needed for those users!
Speficially this issue preactjs/preact#658 has requests for more documentation with the testing story, I bet you we can get our helpers added to their list of good tools whenever we get to publishing this.
Getting the following:
TypeError: document.createDocumentFragment is not a function
at RenderContext (node_modules/preact-render-spy/src/preact-render-spy.js:345:46)
at deep (node_modules/preact-render-spy/src/preact-render-spy.js:373:49)
at Context.<anonymous> (test/components/Burnout.tests.js:12:13)
My mocha/chai setup is:
import chai from 'chai';
import assertJsx from 'preact-jsx-chai';
import 'undom/register';
import 'babel-polyfill';
import 'ignore-styles';
I am using undom as you can see - what dom do you expect?
"devDependencies": {
"babel-plugin-module-resolver": "^2.7.1",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.6.0",
"babel-register": "^6.26.0",
"chai": "^4.1.2",
"eslint": "^4.6.1",
"eslint-config-synacor": "^1.1.1",
"if-env": "^1.0.0",
"ignore-styles": "^5.0.1",
"mocha": "^3.5.0",
"node-sass": "^4.5.3",
"preact-cli": "^1.4.1",
"preact-jsx-chai": "^2.2.1",
"preact-render-spy": "^1.0.0-rc.8",
"sass-lint": "^1.11.1",
"sass-loader": "^6.0.6",
"undom": "^0.3.3"
},
"dependencies": {
"preact": "^8.2.5",
"preact-compat": "^3.17.0",
"react": "npm:preact-compat",
"recompose": "^0.25.0"
}
This was suggested by @developit in the preact slack.
We should allow someone to configure a different fragment creation than document.createDocumentFragment()
I feel like these tests are unnecessarily brittle and hard to follow because of the dynamic class names. Instead, why not simplify by using static class names (or none at all) and change the text, something more like this:
(Note: untested)
it('componentWillReceiveProps', () => {
class Child extends Component {
constructor(props) {
super(props);
this.state = {value: props.value};
}
componentWillReceiveProps(newProps) {
this.setState({value: `_${newProps.value}_`});
}
render(props, {value}) {
return (
<div>{value}</div>
);
}
}
class Parent extends Component {
constructor(props) {
super(props);
this.state = {value: 'default'};
}
render(props, {value}) {
return (
<Child
onClick={() => this.setState({value: 'clicked'})}
value={value}
/>
);
}
}
const context = renderSpy(<Parent />);
const getChild = () => context.find('Child').at(0);
expect(getChild().text()).toBe('default');
getChild().simulate('click');
expect(getChild().text()).toBe('_clicked_');
});
Hello,
I'm trying preact-render-spy
and it does the job very well, thank you for your work.
Right now I'm wondering how would you execute and test code inside a componentWillUnmount
method ?
preact-render-spy/src/is-where.js
Lines 26 to 27 in 28423c6
Upon a couple of context.render(<Component />)
calls, when calling context.find('.class')
I get an exception thrown since target.className
is null
and typeof null === 'object'
.
I am tempted to say that the flow is expected to return false
in the next if
and for that to happen, previous check needs to exclude null
values before checking typeof
.
preact-render-spy/src/is-where.js
Lines 48 to 50 in 28423c6
But I have to say that I don't know why target.className
is null
in the first place.
It would be nice to have a filterWhere function in FindWrapper, that mimics enzyme's functionality.
Hi,
I'm trying to write this bit of code in a test
my-test.test.ts
...
context.find('.search_input').simulate("keyDown", { keyCode: 65 });
...
but the test fail and says:
ReferenceError: event is not defined
at Object.onKeyDown (src/components/search_sale.tsx:199:189)
at FindWrapper.simulate (node_modules/preact-render-spy/src/preact-render-spy.js:252:31)
at Object.test (src/components/search_sale.test.tsx:21:35)
at new Promise (<anonymous>)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
Any suggestions what's wrong? Or simpler, how can simulate a KeyDown? I need to fill an input field
Thank you in advance
RenderContext.render
in a ContextRootComponent
who's state we can mutate with the stuff passed to rendercomponentWillReceiveProps
(copy/pasta from the slack conversation)
gnarf [10:43]
hrm
[10:43]
i think i might of just come up with a way we can support re-rendering props
[10:43]
of the root elems
z [10:44]
nods
z [10:44]
i think that’d be useful too
[10:44]
you could wrap the root in another different spy
gnarf [10:44]
if we made the actual render()
call use a ContextRootComponent
who's state.children we set to whatever is passed to context.render()
[10:44]
then we can just mutate the state on that component
z [10:45]
nods
gnarf [10:45]
and call rerender()
Until it meets a depth that wasn't rendered. I.E.
const Second = () => <div>second</div>;
const First = () => <Second>first</Second>;
expect(deep(<First />).output()).toEqual(<div>second</div>);
expect(shallow(<First />).output()).toEqual(<Second>first</Second>);
It would be nice to have a map
function for FindWrapper
.
This will allow to write tests like this:
expect(wrapper.find('MyComponent').map(n => n.text())).toEqual([
'one',
'two',
'three',
]);
FindWrapper is missing reduce() function.
Lazy web Twitter feature request: https://twitter.com/CPatchane/status/884448320273469445
In index.d.ts, a lot of recent changes have not been applied e.g.
find<Q, T>(selector: string): FindWrapper<Q, T>;
should be
find<Q, T>(selector: preact.VNode | string): FindWrapper<Q, T>;
Some components need to react on componentWillReceiveProps
how would we test that with this interface?
Implement exists
function for FindWrapper
that checks whether or not the current node exists.
The PRs on the new FindWrapper
methods (#53, #57, #59, #63) help show a strata in the FindWrapper
methods. We can categorize the existing and proposed methods as "core" and "extended".
The core methods need knowledge of the internal structure of FindWrapper
to operate.
The extended methods can be implemented with the core methods and without internal knowledge of FindWrapper
.
This can provide some benefits to preact-render-spy
:
FindWrapper
's internal surface area smaller and future changes less prone to regressions on the extended methodsThe following actions need to be performed:
@mzgoddard I think something with using jest-webpack
on this repo is gonna cause us trouble, in order to maintain better compatibility, do we need it? can we avoid it? jest uses something like babel-register to jsx, do we need something else from jest-webpack
?
Like I think #22 could maybe have been avoided?
When i run test, it fails with the TypeError: this.contextRender is not a function when using shallow
error.
This is my test:
import { h } from 'preact'
import { shallow } from 'preact-render-spy'
import { App } from '../../src/app'
describe('App', () => {
test('renders', () => {
const context = shallow(<App />)
expect(context.find('div').contains(<a>link</a>)).toBeTruthy()
})
})
This is error i get:
FAIL test/src/app.test.js
App
✕ renders (4ms)
● App › renders
TypeError: this.contextRender is not a function
5 | describe('App', () => {
6 | test('renders', () => {
> 7 | const context = shallow(<App />)
| ^
8 | expect(context.find('div').contains(<a>link</a>)).toBeTruthy()
9 | })
10 | })
at RenderContext.render (node_modules/preact-render-spy/src/preact-render-spy.js:462:10)
at deep (node_modules/preact-render-spy/src/preact-render-spy.js:472:76)
at shallow (node_modules/preact-render-spy/src/preact-render-spy.js:473:25)
at Object.<anonymous> (test/src/app.test.js:7:21)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 2.196s
Ran all test suites.
When I import:
import {shallow} from 'preact-render-spy';
I get error from babel:
ERROR in ./node_modules/preact-render-spy/index.js
Module build failed: ReferenceError: [BABEL] [cut]node_modules\preact-render-spy\index.js: Unkn
own option: [cut]node_modules\preact\dist\preact.js.h. Check out http://babeljs.io/docs/usage/o
ptions/ for more information about options.
my .babelrc
{
"compact": "false",
"presets": ["env"],
"plugins": [
"transform-decorators-legacy",
"transform-object-rest-spread",
"transform-object-assign",
["transform-react-jsx", { "pragma": "h" }],
"transform-react-jsx-source"
]
}
Any suggestions?
Hi there,
I have an issue with tests when there is a module that are loaded with the !async
keyword from the preact-cli project.
Let's say I have my app.js
that have: import myModule from 'async!./myModule/myModule.js';
If I test an independent module (let's say myOtherModule.js
and not app.js
nor myModule.js
, and myOtherModule
has no information about the async loaded module at all), the test will still fail with: Cannot find module 'async!./myModule/myModule.js' from 'app.js'
Do you know how I can work around this error? (For now, I'm just removing the !async
but that is not ideal :))
Cheers!
Leaving #5 as a place to discuss where and how to announce this package. This Issue is for what the API currently is and how we want it changed to get it to an announcable state.
preact-render-spy/src/preact-render-spy.js
Line 105 in 8752080
This timeout seems really arbitrary, and what if you have fake timers going or something?
We could instead use preact.rerender
which will render all dirty items in the queue: https://github.com/developit/preact/blob/d213b086e36e0ec3c4700d36a5e6a3368af74894/src/render-queue.js#L15
It would be nice to have first
and last
functons for FindWrapper just like in enzyme
Hello! I was trying the latest rc.7 to port my snapshot tests from manually use preact-render-to-string
to this library built-in solution.
One glitch I found is when I try to snapsnot an element which uses preact-i18n.
This is my component method:
export const Select = ({ name, value, options, htmlFor, onBlur, onChange }) => {
return (
<select
name={name}
value={value}
onChange={onChange}
onBlur={onBlur}
id={htmlFor}
>
<option value=""><Text id="select">Select</Text></option>
{options.map(({ value, label }, i) => <option key={i} value={value}>{label}</option>)}
</select>
);
}
This is the output of preact-render-to-string/jsx
:
<select
name="foo"
id="foo"
>
<option value="">Select</option>
<option value={1}>bar</option>
<option value={2}>baz</option>
</select>
While this is the output of preact-render-spy
snapshot:
preact-render-spy (1 nodes)
-------
<select
name="foo"
id="foo"
>
<option value="">[object Object]</option>
<option value={1}>bar</option>
<option value={2}>baz</option>
</select>
Notice how the <Text id="select">Select</Text>
is render as [object Object]
in the snapshot.
Not sure if that's a problem only of preact-i18n
though.
hey there,
if you do the following:
import { h, Component } from 'preact';
class Example extends Component {
render() {
return (<div>hello, world</div>);
}
}
Example.defaultProps = {
foo: 'bar'
}
export default Example;
and then try to inspect the nodeName
property of this class instance via RenderContext
, you get an interesting looking object like this:
{
[Function: Example]
defaultProps: { foo: 'bar'}
}
I'm unsure what exactly is going on here. It looks like the component (as a function) is being assigned as a key to the nodeName
property? how do we access this, or assert against it?
if you do this:
console.log( JSON.stringify(context.nodeName) );
it returns
undefined
Hi! First, thank you for your work)
I have a problem, I cannot find by any data-attributes.
For example:
const context = shallow(<SomeComponent />);
console.log(context.find('[data-target="id"]')); // FindWrapper { length: 0 }
And .find always return 0 nodes (although this element exist).
I tried different cases: .find('[data-target="id"]')
, .find('div[data-target="id"]')
, .find('data-target="id"')
. But nothing.
Where is my mistake?
Implement children
and childAt
functions for FindWrapper
.
We currently have .output()
which returns a VNode, but the more I look at this, the more I think it might of been a mistake to not return a FindWrapper
instance here. We need a method that can return the FindWrapper version of component output, and we've been talking about it over in #57 PR comments.
I wouldn't be entirely against going to a 2.0 and making .output()
return a FindWrapper, and .nodes()
returning VNodes or something, but if we can come up with a good name for outputWrapped()
we might not need to break back-compat.
Hi guys Im having a weird problem where my component receive a function parameter as undefined if:
It is not declared in the same scope
OR
I don't declare and pass other variables on the same scope
describe('myTest', () => {
let myComponent
let myMock
beforeEach(() => {
myMock = jest.fn()
myComponent = shallow(
<MyComponent action={myMock} />
)
})
})
FAIL this.props.action is undefined
describe('myTest', () => {
let myComponent
beforeEach(() => {
let myMock = jest.fn() // declared on the same block
myComponent = shallow(
<MyComponent action={myMock} />
)
})
})
WORKS this.props.action is my mock
describe('myTest', () => {
let myComponent
let myMock
beforeEach(() => {
myMock = jest.fn()
let anything = 'anything' // anything else declared and passed in the same block
myComponent = shallow(
<MyComponent action={myMock} anything={anything}/>
)
})
})
WORKS this.props.action is my mock
my deps:
{
"jest": "^23.6.0",
"preact-cli": "^2.2.1",
"preact-render-spy": "^1.3.0",
"preact": "^8.4.2"
}
any ideas?
thanks
Loving preact-render-spy!
I would like to contribute, and thought maybe we can start brainstorming ideas for developer experience/ergonomics for the wiki, as writing tests can sometimes be a little tricky.
One of the issues that I have is that I want to see the HTML of the component, for development purposes. This would be handy for seeing exactly what the component is outputting.
How would I do this? Something like:
const context = shallow(<Testable />);
console.log(context.output().html())
What other ideas do other folks have?
Let's say I've a component like this:
class Button extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
if (this.state.loading){
return <span>Loading ...</span>;
}
return <button onClick={() => this.setState({ loading: true })}></button>;
}
}
How can I test that when loading
state prop is true
the loading message is displayed?
It would be super useful to have something like enzyme's setState on the component. What do you think about this?
I'm using this to test some code in TypeScript
, however when I use .attr('id')
, I get a compile time error:
Argument of type '"src"' is not assignable to parameter of type 'never'.
Example below:
function Text({ id, children }: IProps) {
return <p id={id}>{children}</p>
}
...
const instance = shallow(<Text id="test" />);
const idValue = instance.find('p').attr('id'); // error
Thanks in advance for the help!
Hi everyone!
I'm working with preact and I'm currently doing a dependency injector for some config we have in our app. I decided to use preact-context for the new api and I'm using it through a higher order component that consumes the dependencies and injects it onto the wrapped component.
The problem I'm facing is not testing the HOC itself, but rather, when we test a wrapped component that is part of another component. We usually use shallow
for testing specially on the higher level components like the page containers. The problem I'm facing is that now we have a another layer with each component created with the HOC. I've read that enzyme has a dive
function that allows you to go further down the hierarchy to the wrapped component even though it is not covered by the initial shallow.
Is there something similar here or any other way to replicate that behavior here?
Hi,
Thanks for this. It looks cool.
I get this TypeError: Cannot read property 'nodeName' of null
error though. I haven't looked at all the code yet but it looks like you're recursively checking the vdom and at some point you try to check the nodeName of null
I think we should add a test on vdom being null
here: https://github.com/mzgoddard/preact-render-spy/blob/master/src/preact-render-spy.js#L10
It seems to work when I add this at the beginning of spyWalk
:
if (vdom === null) {
return '';
}
Does it make sense ?
Hi, firstly, looks like an ace library, I was just struggling with enzyme + preact this week!
I've found a potential bug, but it may be that I've not got the correct setup as there doesnt seem to be an issue when running the tests in this repo.
Running a simple test with Jest gives this error for me, minimal reproduction here
node_modules/preact-render-spy/src/preact-render-spy.js:19
),
^
SyntaxError: Unexpected token )
its fixed by removing the trailing comma after nodeName
on this line
When using functional components, defaultProps do not get applied. When switching to a class component, there are no problems. I've provided a minimal example below w/ the output of running the test suite.
const propTypes = {
price: PropTypes.number
};
const defaultProps = {
price: 0
};
function Price(props) {
const { price } = props;
return (
<div className="price">
${price.toLocaleString()}
</div>
);
}
Price.propTypes = propTypes;
Price.defaultProps = defaultProps;
export { Price };
import { shallow } from 'preact-render-spy';
import { Price } from '../components/Price';
describe('<Price>', () => {
test('it renders default price', () => {
const snapshot = shallow(<Price />);
expect(snapshot.find('.price').text()).toBe('$0');
});
});
TypeError: Cannot read property 'toLocaleString' of undefined
at Price (src/components/Price.jsx:51:14)
at new <anonymous> (node_modules/preact-render-spy/src/preact-render-spy.js:67:30)
at x [as render] (node_modules/preact/dist/preact.min.js:1:5403)
at k (node_modules/preact/dist/preact.min.js:1:6208)
at k (node_modules/preact/dist/preact.min.js:1:6455)
at i (node_modules/preact/dist/preact.min.js:1:965)
at RenderContext.ContextRootWrapper.vdom (node_modules/preact-render-spy/src/preact-render-spy.js:423:7)
at RenderContext.render (node_modules/preact-render-spy/src/preact-render-spy.js:462:10)
at deep (node_modules/preact-render-spy/src/preact-render-spy.js:472:76)
at shallow (node_modules/preact-render-spy/src/preact-render-spy.js:473:25)
at Object.<anonymous> (src/tests/Price.test.js:13:21)
at process._tickCallback (internal/process/next_tick.js:109:7)
Say you have a component that has a boolean prop:
const Hello = ({ isActive }) => <div style={{ display: isActive ? 'block' : 'none' }}>Hello</div>
<Hello isActive={false} />
In such a case, calling shallow(<Hello isActive={false} />).attr('isActive')
will return undefined
instead of the false
you'd expect.
The reason for this is this line, which checks the truthiness of item.attributes[name]
, which this case fails, thus returning undefined
.
Thank you for this wonderful project.
I just wanted to ask, if the behavior of text() not working with input elements is intended?
For example if we have a component with this output:
"preact-render-spy (1 nodes)
-------
<td class="column-text-centered input_price">
<div>
<input
type="text"
value="5,00"
onBlur={[Function debounced]}
class="defaultInputStyle defaultUnderline customPriceInput"
/>
<span>EUR</span>
</div>
</td>
"
a .text() call on .input_price will only return the span elements text but I would "expect" that it would also return the value of input? Technically it is not a plain Text Dom element but I would like to ask if it would make sense to include input (maybe also other input elements) to the .text() functionality?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.