How to unit test permissions in a React Admin controller? - jestjs

I'm using react-admin and I have a list component that has some conditional display behavior based on permissions.
I'd like to write a simple Jest test that asserts the correct behavior (display the Edit button if admin, hide the Edit button if not admin), but I need to feed some mock records into my list component. How do I pass mock data to my list component?

There is (now) a dedicated paragraph on the documentation about testing permissions-based view: https://marmelab.com/react-admin/UnitTesting.html#testing-permissions
I also wrote an example of unit test available on the code base: UnitShow.spec.js
it('should show the user role in the second tab', () => {
const wrapper = shallow(<UserShow permissions="admin" />);
const tabs = wrapper.find(Tab);
const fields = tabs.at(1).find(TextField);
expect(fields.at(0).prop('source')).toBe('role');
});

Related

How do I use Jest to test that one text element comes before another?

I have a list I'm rendering in my React app, and I need to test that I'm listing the list items in alphabetical order.
Initially I tried testing this by querying the document this way:
const a = getByText("a_item");
const el = a.parentElement?.parentElement?.nextSibling?.firstChild?.textContent;
expect(el).toEqual("b_item");
But this proved to be brittle. I don't want to test the HTML structure of each item. I only want to test that the list is alphabetical.
How can I test that the list is alphabetical without depending on the current HTML structure of the list?
Use String.search to find the indices of the strings in the document's HTML, and then assert that indices are in the correct order:
it("lists items alphabetically", async () => {
loadItems([
"b_item",
"a_item",
]);
await render(<App/>);
await waitFor(() => {
const html = document.body.innerHTML;
const a = html.search("a_item");
const b = html.search("b_item");
expect(a).toBeLessThan(b);
});
});
Note that this may not be ideal since it accesses the dom directly, which isn't considered best practice when using React Testing Library. I haven't tested this, but it would probably be better to use a regex matcher with a built-in React Testing Library query method:
it("lists items alphabetically", async () => {
loadItems([
"b_item",
"a_item",
]);
await render(<App/>);
expect(await screen.findByText(/a_item.+b_item/)).toBeInTheDocument();
});

DevExtreme DataGrid react-testing-library

Cannot test datagrid component
Here is example.
https://codesandbox.io/s/g921o?file=/src/components/Home.js
Please check test console. screen.debug() doesn't return dom with dataSource of data grid.
How to test data grid with simple array or with CustomStore.
In my case this way helped, but then there is a problem of how to get updated number of rows if some action happened...
it("We are doing test1", async () => {
render(<Home />);
const rows = await screen.findAllByText(/Test\d/);
console.log(rows);
});
HERE they answer why the regular way doesn't work for their components...

Persist data sent to a Pug template via render

I'm trying to find out how I can persist the data I pass to my Pug template from the Express render method.
I pass in some JSON data to the res.render() method in Express that renders my view with Pug. On the Pug template, I use that data immediately to populate one of my select elements with drop down values from the JSON data.
What I want to then do is store this data that was passed so I can use it in an event handler function I create for another field.
Basically, I'm passing a table name and the field names for the table, but for each table I have in the JSON data.
So the shape is like [{ tableName: "table name here", fieldNames: ['field1', 'field2', ...] }, ... ]
I have a select field for "choose a table name" and when the user picks a table name, I then want to get the fieldNames for a second select field that allows them to choose a field name to use. So I have an event handler setup on the "choose a table name" field that runs a little event handler I have setup in the pug template. Only problem is the event handler does not have access to the data that was passed to the Pug template originally.
I'm trying to google this but having no luck finding anything, so does anyone know how I can persist data sent via the res.render() method in a pug template for using after the page has been rendered inside an event handler or other functions?
Thank you!
Always be clear what is done at the server (pug) and what is done in client Javascript (browser).
While data passed to pug scripts are meant to be consumed at the server, it is possible to inject, for want of a better word, server data into client side Javascript variables.
The following creates two dropdown lists on the same page using the exact same data passed by Express. One is generated at the server, while the second is created entirely by Javascript running in the browser.
Express code:
app.get("/testdata", (req, res) => {
res.render("testdata", { data: [ 1, 2, 3, 4, 5]});
});
testdata.pug:
html
head
body
p Dropdown list generated at the server:
p
select
each n in data
option(value=n)=n
br
p Dropdown list generated in browser Javascript:
p
select#dropdown
script.
document.body.onload = () => {
const dropdown = document.getElementById("dropdown");
let data = JSON.parse(`!{JSON.stringify(data)}`); // line 18
data.forEach(d => {
const item = document.createElement("option");
item.innerText = d;
dropdown.appendChild(item);
})
}
I have used the same variable name in totally different contexts pointing to different entities. Be careful not to trip. For example, look at line 18:
let data = JSON.parse(`!{JSON.stringify(data)}`); // line 18
The first instance of data is a Javascript variable in the browser.
The second instance of data is a server object passed to the pug script in the render method. Basically any !{expression} instances found in a pug file are evaluated¹ when the view is rendered.
¹ I think the expression is evaluated and its toString method called. If I know it is an array, I could have used:
let data = [!{data}]; // line 18

Hide Product Catalog standard dashlet for all users in Sugarcrm

How can i hide a standard dashlet named "Product Catalog" from the list which gets displayed in the drawer named "Add a Sugar Dashlet". "Add a Sugar Dashlet" drawer gets displayed when user tries to add a dashlet in any dashbaord in Sugarcrm. Hiding should be done in an upgrade safe way.
Note: I am using Sugarcrm Ver 8.0.0 PRO
One way to accomplish this is by creating a custom override of the DashletselectView where you filter out the Dashlet in question.
The code below does so by overriding an internal function of the view, post-processing its results.
custom/clients/base/views/dashletselect/dashletselect.js
({
extendsFrom: "DashletselectView",
_getDashlets: function() {
var dashlets = this._super("_getDashlets", arguments);
return _.filter(dashlets, function (d) { return d.type !== "product-catalog-dashlet"; });
},
})
Then run Quick Repair & Rebuild so that Sugar detects the presence of the custom file and loads it.

use drupal hook_nodeapi

i have a page tweety which contains a single form where a user enters a word in a textbox and in on pressing search the tweets corresponding to that word are displayed.
I want to use hook_nodeapi to add these tweets but i want those things only on a specific url not all the page(or any node type).
I ll use hook_menu to make that page and display the form for the search. What should i do after that. I knw the twiiter api to fetch the tweets so that is not a issue.
I think what you're attempting to do is have the tweets load on a predetermined page by passing the posted information to the page, is that correct?
There's a couple ways I think you could accomplish this - easiest, and not requiring any complex ahah scripting would be to take the word they enter and generate a path which you could then use to make the page in question.
Make a menu Item:
function mymodule_menu() {
$items = array();
$items['mymodule/tweets/%'] = array(
'title' => t('Tweets about' . check_plain(array(2))),
'page callback' => 'mymodule_tweet_display',
'page arguments' => array(2) // This is the 3rd argument in the path, the %
);
}
In your form, you'll need to send the user to this path so add to your hook_submit function:
$tweet_topic = $form_state['values']['your-field-name-here'];
drupal_goto('mymodule/tweets/' . $tweet_topic);
Now add your page module, this is where you can use the twitter api:
function mymodule_tweet_display($tweet_topic) {
$tweet_topic = check_plain($tweet_topic);
// Use this variable in your Twitter API call
// ...
// Make sure you assign your display content to the page by returning a variable
return $page;
}

Resources