Giter VIP home page Giter VIP logo

resq's People

Contributors

baruchvlz avatar bkirova avatar dependabot[bot] avatar jens-duttke avatar mkilp avatar mvasin avatar pxyup avatar tobloef avatar zt-sv avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

resq's Issues

node incorrectly being returned as an empty array

We have a React component named EditableCallRoutingTableDialog that contains within it another of our React components named EditableCallRoutingTable. The relevant snippets showing the code for these components is shown below. We added a call to React.useEffect in the EditableCallRoutingTable component as follows:

  React.useEffect(() => {
    console.log(resq$$('EditableCallRoutingTabl*', document.getElementById('root')));
  }, []);

in order to see what resq$$ returns for our components and got this output:

r(2) [{…}, {…}]
0:
children: [{…}]
name: "EditableCallRoutingTableDialog"
node: div.MuiDialog-root
props: {announcements: Array(11), routes: Array(2), rows: Array(1), basicRoutingService: false, dialogOpen: true, …}
state: {memoizedState: {…}, baseState: null, baseQueue: null, queue: null, next: {…}}
[[Prototype]]: Object
1:
children: (2) [{…}, {…}]
isFragment: true
name: "EditableCallRoutingTable"
node: []
props: {announcements: Array(11), routes: Array(2), rows: Array(1), basicRoutingService: false, editedCells: Set(0), …}
state: {memoizedState: {…}, baseState: null, baseQueue: null, queue: null, next: {…}}
[[Prototype]]: Object
length: 2
lastIndex: (...)
lastItem: (...)
[[Prototype]]: e

From the output above for EditableCallRoutingTableDialog we see that the node field is returned as div.MuiDialog-root which matches what we expected.

However, the node field for the EditableCallRoutingTable seems to be an empty array when we expected it to be a div.

If we remove the Snackbar component from the end of the EditableCallRoutingTable code then the node is returned as:

node: div.MuiPaper-root.MuiCard-root.makeStyles-tableContainer-2595.MuiPaper-elevation1.MuiPaper-rounded

which looks correct.

Are we using your library incorrectly or is this a bug?

We are using:

  • react 16.14.0
  • @material-ui/core 4.12.4
  • resq 1.10.2

Code Snippet for EditableCallRoutingTableDialog

...
export const EditableCallRoutingTableDialog: React.FC<EditableCallRoutingTableDialogProps> = ({
  announcements,
  routes,
  rows,
  basicRoutingService,
  dialogOpen,
  onRefreshAnnouncementsClicked,
  onUploadAnnouncementClicked,
  useLazyGetAnnouncementSignedGetUrlQuery,
  onSaveClick,
  onCancelClick
}) => {
  ...

  return (
    <Dialog
      PaperProps={{ classes: paperClasses }}
      fullScreen
      maxWidth={false}
      open={!!dialogOpen}
      onClose={() => false}
      disableEscapeKeyDown
    >
      <DialogTitle className={classes.dialogTitle} title="Edit" />
      <DialogContent className={classes.dialogContent}>
        <section className={classes.headerContents}>...</section>
        <EditableCallRoutingTable
          announcements={announcements}
          routes={routes}
          rows={rows}
          basicRoutingService={basicRoutingService}
          editedCells={editedCells}
          changedRows={changedRows}
          changedRouteOrdinals={changedRouteOrdinals}
          setHasChanged={setHasChanged}
          onRefreshAnnouncementsClicked={onRefreshAnnouncementsClicked}
          onUploadAnnouncementClicked={onUploadAnnouncementClicked}
          useLazyGetAnnouncementSignedGetUrlQuery={useLazyGetAnnouncementSignedGetUrlQuery}
        />
      </DialogContent>
    </Dialog>
  );
};

Code Snippet for EditableCallRoutingTable

...
export const EditableCallRoutingTable: React.FC<EditableCallRoutingTableProps> = ({
  announcements,
  routes,
  rows,
  basicRoutingService,
  editedCells,
  changedRows,
  changedRouteOrdinals,
  setHasChanged,
  onRefreshAnnouncementsClicked,
  onUploadAnnouncementClicked,
  useLazyGetAnnouncementSignedGetUrlQuery
}) => {
  ...
  React.useEffect(() => {
    console.log(resq$$('EditableCallRoutingTabl*', document.getElementById('root')));
  }, []);

  return (
    <>
      <Card className={classes.tableContainer}>
        <div className={classes.table}>
          <DataTable customStyles={rdtCustomStyles(theme, disabledColumns)} columns={columns} data={data} />
        </div>
      </Card>
      <Snackbar
        variant="error"
        message={getAnnouncementSignedGetUrlQueryResult.error?.message}
        show={!!playingAudio && !!getAnnouncementSignedGetUrlQueryResult.error?.message}
        verticalAnchor="top"
        onClose={() => setPlayingAudio(undefined)}
      />
    </>
  );
};

resq queries should consider the HTMLElement (node) objects rather RESQ objects

Hi @baruchvlz

The JSX:

import React from 'react';
import { getProducts } from '../products';

class AProduct extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      myName: props.name,
      orderCount: 0,
    };
  }

  order() {
    this.setState({
      orderCount: this.state.orderCount + 1,
    });
  }

  render() {
    return (
      <div className="product">
        <span className="name">{this.state.myName}</span>
        <span className="ordered">{this.state.orderCount}</span>
        <button className="order" onClick={this.order.bind(this)}>
          Order
        </button>
      </div>
    );
  }
}

const Products = ({ products }) => (
  <React.Fragment>
    {products.map((product) => (
      <AProduct key={product.id} name={product.name} value="product" />
    ))}
  </React.Fragment>
);

class ProductsContainer extends React.Component {
  state = {
    products: [],
  };

  componentDidMount() {
    // for now use promises
    return getProducts().then((products) => {
      this.setState({
        products,
      });
    });
  }

  render() {
    return (
      <div className="product-container">
        <Products products={this.state.products} />
      </div>
    );
  }
}

export default ProductsContainer;

FETCH Products:

export const getProducts = () => {
  console.log('fetch products');
  return fetch('http://myapi.com/products')
    .then((r) => r.json())
    .then((json) => {
      console.log('products', json.products);
      return json.products;
    });
};

The outcome:

Screenshot 2020-11-20 at 2 28 59 PM

Expectation:
As per (Type definition)[https://github.com/baruchvlz/resq#type-definition], node returns the real HTMLElement so resq$ /resq$$ should consider the node rather than the RESQ object

Now, surprisingly, for a different react example, everything works as expected.

Screenshot 2020-11-20 at 2 35 33 PM

So, what's really happening for the above example that RESQ is behaving differently?

getting window not defined error

hi,
I am getting window not defined error while importing the resq module. I am doing nothing fancy, just running:

import { resq$ } from 'resq';

console.log(resq$('MyComponent', null));

npm script: ts-node test.ts

Error thrown:

ReferenceError: window is not defined
    at Object.<anonymous> (C:\Dev Room\Projects\node_modules\resq\dist\index.js:1:221)
    at Module._compile (internal/modules/cjs/loader.js:701:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
    at Module.load (internal/modules/cjs/loader.js:600:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
    at Function.Module._load (internal/modules/cjs/loader.js:531:3)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:22:18)
    at Object.<anonymous> (C:\Dev Room\Projects\test.ts:3:16)
    at Module._compile (internal/modules/cjs/loader.js:701:30)

React 18 support?

According to an issue reported on the downstream cypress-react-selector package abhinaba-ghosh/cypress-react-selector#320 (comment) there seems to be a problem with the latest 1.10.2 release of resq with React 18. The reason seems to be a change in react component/object key naming.

Can you please support React 18? Thank you very much!

state field returns only the value of the first useState call

When getting the state using getElementState(element.memoizedState) here in the source code, it seems like only the first useState value is considered instead of gathering all values of useState calls together into an array using the next field on the memoizedState object which when not null, holds the value of the next useState call

Allow for wildstar selector

Scenario:

// imports

const MyComponent = () => (
  <>
    My Component
    <div>Nested Div</div>
    <div>Nested Div</div>
    <section>Nested Div</section>
  </>
);

const App = () => (
  <div>
    <MyComponent />
  </div>
);

ReactDOM.render(<App />, document.querySelector("#root"));
const myComponentChildren = await resq$$('MyComponent *')
/*
        outputs:
        [
            {
                children: Array,
                name: String,
                node: <div />,
                props: Object,
                state: Object
            },
            {
                children: Array,
                name: String,
                node: <div />,
                props: Object,
                state: Object
            },
            {
                children: Array,
                name: String,
                node: <section />,
                props: Object,
                state: Object
            }
        ]
   */

Won't Find Root If React Mounted Asynchronously

Related to Webdriverio issue 3916.

If react is mounted asynchronously. Using react$ selector throws the error:
javascript error: Could not find the root element of your application.

I see two cases for mounting react asynchronously:

  1. React is only used for a subset of the app.
  2. React needs to request for external data before it is mounted.

Handle not found element more gracefully

Currently we just throw an error. We should let the user know that one of the possibilities is an anonymous component along side saying that the component was not found

Add sourcemap support

This might be a bit tricky but definitely solve able. It would be cool if resq could fetch the sourcemaps of the bundled react files and allow to fetch the original component names even though the code is minified.

Missing types

Hello, I'm playing with your library, and I noticed that the properties created for the interface RESQNode must be wrong. There are no byProps or byState properties. It is possible to bend component to any and then call byProps and it will work, but it would be better if your javascript functions are offered the same typescript types.

const nodes = resq$(name);
const component = (nodes as any).byProps({a: 1});

Would be great if you can fix this, thanks!

byProps/byState should match all keys

Current behavior

When passing a matcher with multiple keys, .byProps/.byState returns nodes that match any one of the given keys.

Example:

const filtered = myComponent.byProps({ prop1: 123, prop2: 'abc' })
console.log(filtered)
/*
{
    name: 'MyComponent',
    props: {
        prop1: 123,
        prop2: 'def',
    },
    // ...
}
*/

Expected behavior

.byProps/.byState returns nodes that match ALL of the given keys.

Example:

const filtered = myComponent.byProps({ prop1: 123, prop2: 'abc' })
console.log(filtered)
/*
{
    name: 'MyComponent',
    props: {
        prop1: 123,
        prop2: 'abc',
    },
    // ...
}
*/

Failing test case: ooorayray@b502b83

General question

Hi guys, this utility is the most awesome thing I've tried out this week.

Since you seem to be knowledgable of react stuff I just wanted to ask if you know some generaliseable way to trigger a rerender? Because if I find a component with this and change its props the real DOM doesn't get updated and it would be nice to be able to do that. Are there any methods I could call?

docs: Missing information on `exact` flag

Both filters (byProps and byState) have an exact flag which will deep evaluate the objects and return true only if all values match.

This information is missing from the documentation.

IE11 not identifying elements properly

Hi,

I was playing with resq and I have encountered functional differences in different browser. Chrome and Firefox greatly handles the react component identification part. But, not IE11.

I have changed res$$ to resqTT, as IE can't handle those symbols. But, this is not a showstopper.

I have attached the findings for two different browsers for same website:

website under test: https://react-shopping-cart-67954.firebaseapp.com/

Chrome:

image

you can clearly see that I am getting the element and element count is 1

*IE11:

image

Now, element size is 0

Any help to mitigate this issue will be a great help. I need to run resq in IE anyway.

Strip HoCs from component name before matching selector

Currently, if we want to select a styled component (for example) we would have to first find out the displayName from the React dev tools and then select it with the proper HoCs wrapped around.

Given this example component

// index.jsx
// imports

export default const Button = styled.a`...`
// wrapping the HoCs
resq$('Styled(Button)', document.getElementById('root'))

// ideally
resq$('Button', document.getElementById('root'))

// in a perfect world, both would work
resq$('Styled(Button)', document.getElementById('root'))
resq$('Button', document.getElementById('root'))

This is not the best solution considering that it can be confusing having to download React dev tools, get used to it, and then find the component, etc.

If we strip these HoCs from the displayName property before matching the selectors, then we wouldn't need to wrap the HoCs around our selector component.

Not able to identify RESQ nodes for non-root element

RESQ version: 1.8.0

If root is used while fetching the resq nodes, it is able to identify the child elements. But, if non-root element is used, it is failing to identify the same. The attached screenshots will be more descriptive:

DOM:
Screenshot 2020-09-21 at 6 24 17 PM

Fetch Commands:
Screenshot 2020-09-21 at 6 19 23 PM
So, in first command it is not able to identify the child elements. Once the root is used, both the child products got identified.

Todos before v1

  • Update README.md to explain the difference between selecting components using React.Fragment and those using HTML elements as wrappers
  • Add CI with Codecov
  • Add minimum coverage for files
  • Add type definitions
  • Add commitizen cli and changelog

Optional

  • Rewrite to not use try/catch this way we don't need @babel/runtime and we can reduce the size of the library by 40% (bundlephobia)

Use webpack for bundling

The current bundled version is not useful as it still requires some babel packages. I got a much smaller bundle that assigns to the window object by using webpack and the following config:

const path = require('path')

module.exports = {
    mode: 'production',
    entry: './index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'resq.bundle.js',
        library: ['window', 'resq'],
    },
}

To be able to use it with WebdriverIO it would be great there would be a bundle that assigns itself to the window object. With that I can inject it using the executeScript command and run queries from the window object, e.g. https://github.com/webdriverio/webdriverio/pull/3797/files#diff-d58e668c0402c2fa779bbf9c90c308a3R5

Function waitToLoadReact is resolving when global.rootReactElement is undefined

The function waitToLoadReact is resolving even when the global.rootReactElement is undefined instead of retrying till the timeout.
https://github.com/baruchvlz/resq/blob/master/src/waitToLoadReact.js

tempsnip

Even when the findReactInstance function returns undefined the promise is resolved.

This issue is provoking abhinaba-ghosh/cypress-react-selector#104 in the cypress-react-selector module.

In our case we have a next.js project.

Repro steps would be just:

cy.visit("/")
cy.waitForReact(1000, `#__next`);
cy.react("Logo"); // Some dummy component mounted in the index page

The bug is reproducible 1 of 10 times in a run of total 100 tests(we just generate the above test 100 times to repro the bug).

Doesn't work with `displayName` of functional components

To be able to see the "real" component names in the compiled version of my webapp (https://hexed.it) I'm using displayName, like here:

export const GridItem: React.FunctionComponent<GridItemProps> = (props) => {
	// [...]   
};

GridItem.displayName = 'GridItem';

While the ReactDevTools is showning the displayName of the component correctly, resq is returning the compiled 1-letter-name.
If I change the getElementName() function:

return isFunction(type) ? type.name : type

to something like:

return isFunction(type) ? (type.displayName || type.name) : type

it works fine.

element.node is null

Recently came across an issue with our react app and webdriver.io returning elements without the underlying node. The node value is null as shown below:

Screen Shot 2019-06-28 at 12 30 50 PM

This seems to point to $resq. More details here:
webdriverio/webdriverio#4129

Any ideas what could be the issue?

some ideas

Hey @baruchvlz ,

thanks for working on this! I looked the code and wanted to share some idea. Maybe I get the time to contribute to this project and make PRs for them.

  • in index.js - await waitToLoadReact(5000): maybe we can make the timeout configurable but 5000ms seams a reasonable default
  • in getRootComponent - do all root elements have the id '#root'?
  • instead of expecting the root component to have the id root we could let waitToLoadReact return the root element because it will look for it anyway, no?
  • to make this usable in WebdriverIO it will be necessary that this page bundles the entrypoint into a single (minfied) file so I can inject it into the page and use it afterwards

Possibility of updating props and states in runtime

I know the library is built to provide the Node information to the end user by quering React properties. I was just thinking about any possibilities of updating props and states using some crazy commands - setProps or setState. With Enzyme, it can be possible by mounting/shallowing the target component.But currently there is no solution where somebody doesn't have the access to the source code, like in e2e testing or integration testing.

can't find element by props in 1.9.0

Steps to reproduce
in browser console
resq.resq$$('*', document.getElementById('root')).byProps({name: '5'})

Actual Result
returned 44 elements

Expected Result
one element

WebdriverIO steps to reproduce

browser.url('https://ahfarmer.github.io/calculator/')
const el = browser.react$('*', { props: { name: '5' } })
console.log(el.getTagName(), el.getText())

Actual Result

div 0
AC
+/-
%
÷
7
8
9
x
4
5
6
-
1
2
3
+
0
.
=

Expected Result
div 5

Bunch of suggestions

I am not sure if creating separate issue for all of these makes sense, but these are roughly the concerns I have while looking through the code and underlying FiberNode structure -

  1. No direct access to underlying FiberNode.
    The output is a simplified structure (duplicated and subset), which might be great for common testing usecases. But it has lot of downsides.
    Like:
    a. Power users can't do something more interesting (like access the class instances, present in stateNode) with the simplified output (being subset).
    b. It duplicates whole subtree (of FiberNode) into the simplified structure, which might be a complete waste if I only want to access the first node (being duplicated).
    c. You can't "resume" or chain search from the output of a previous search. For ex., resume means you can find a component from the first search and use the output to do a second search which is within the found component (being subset). To give a concrete example document.querySelector("#outer").querySelector("#inner").
  2. No proper CSS selector supported. Only some degree of nesting like classA classB is supported, but definitely not something like classA:last-child <Optional>
  3. Expects browser env with global tryToFindApp. SHould allow both root DOMNode and class component ref.
  4. Component objects or instances can't be used for searching. Only names are supported
  5. No proper types present for others, who will try to access the output. <Optional>

Optional marked items are good to have and can be build on top, but other things can be blocker for some usecases and can't be solved in userland.

RESQNode class exposes cached `nodes`

When creating a RESQNode instance, we keep nodes in memory in order to be able to do resq$('SomeComponent').byProps({ /* ... */ }).

These nodes should be a private property in the class

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.