I am trying to test my table but I can't seem the access the table headers? I am testing that 6 exist as seen in the logs below.
TestingLibraryElementError: Unable to find an accessible element with the role "th"
Here are the accessible roles:
import { render, screen } from '#testing-library/react'
import '#testing-library/jest-dom'
import Table from "../Table/Table"
describe('Table', () => {
beforeEach(() => {
render(<Table />)
})
it('should render headers', () => {
screen.logTestingPlaygroundURL()
expect(screen.getAllByRole('th')).toHaveLength(6)
})
})
<table class="sc-dkrFOg eudlDK">
<thead class="sc-iBYQkv fBmNxW">
<tr class="sc-hLBbgP imKlCt">
<th class="sc-eDvSVe jtchdi">
ID
</th>
<th class="sc-eDvSVe jtchdi">
NAME
</th>
<th class="sc-eDvSVe jtchdi">
</tr>
</thead>
<tbody class="sc-gKPRtg"/>
</table>
I have tried reading various docs online.
This works. I am accessing via columnheader.
it('shoulda colum with id', () => {
expect(screen.getByRole('columnheader', { name: /id/i })).toBeInTheDocument();
});
Related
I want to get the list of IP from a website and add them into an array. The website shows the data like this:
<tbody><tr role="row" class="odd">
<td>131.108.216.44</td>
<td>47267</td>
<td>BR</td>
<td class="hm">Brazil</td>
<td>elite proxy</td>
<td class="hm">no</td>
<td class="hx">yes</td>
<td class="hm">2 minutes ago</td>
</tr>
<tr role="row" class="even">
<td>85.173.165.36</td>
<td>46330</td>
<td>RU</td>
<td class="hm">Russian Federation</td>
<td>elite proxy</td>
<td class="hm">no</td>
<td class="hx">yes</td>
<td class="hm">2 minutes ago</td>
</tr>
</tbody>
This is actually a very long list with 100's of table but the format is the same.
What I did is :
var c = new Crawler({
maxConnections: 1,
callback: function (error, res, done) {
if (error) {
console.log(error)
} else {
var $ = res.$;
$('tbody>tr>td').each((i, el) => {
const item = $(el)
console.log(item.text());
})
}
done();
}
})
c.queue({
uri: 'https://free-proxy-list.net/'
})
I want to keep the first 10 IPs from the website and add them into an array.
The first 10 would look like this:
let proxies = $('tr[role=row]').map((i, tr) => {
let host = $(tr).find('td:nth-child(1)').text()
let port = $(tr).find('td:nth-child(2)').text()
return `${host}:${port}`
}).get().slice(0, 10)
I have a parent and a child components. There creates a table with data. I have a modify icon in every row of a table. Now I need when this icon is clicked - there were created input fields for this particular row and this data can be modified, then if pressed "Ok" - pass this changed data to database with "axios.put" method, and if pressed "cancel" - do note modify item.
I created isInEditMode variable. But I don't know where to create that input fields, in child or in parent?
Child:
import React from 'react';
import './tableHasp.css';
class TableHasp extends React.Component {
render(){
return (
<tr>
<td>{ this.props.hasps._id}</td>
<td>{ this.props.hasps.serial }</td>
<td>{ this.props.hasps.soft }</td>
<td>{ this.props.hasps.numberOfKeys }</td>
<td>{ this.props.hasps.company.name }</td>
<td>{ this.props.hasps.company.city }</td>
<td>{ this.props.hasps.company.phone }</td>
<td>{ this.props.hasps.dateCreated }</td>
<td><i onClick={() => this.props.modifyEvent(this.props.hasps)} className="far fa-edit btnEdit"></i></td>
<td><i onClick={() => this.props.delEvent(this.props.hasps._id)} className="far fa-trash-alt btnDelete" ></i></td>
</tr>
);
}
}
export default TableHasp;
Parent data for child:
<div className="container">
<table className="table table-striped">
<thead className="thead-dark">
<tr>
<th scope="col">id</th>
<th scope="col">Serial Number</th>
<th scope="col">Soft</th>
<th scope="col">Number of Keys</th>
<th scope="col">Company</th>
<th scope="col">City</th>
<th scope="col">Contacts</th>
<th scope="col">Date Created</th>
<th scope="col">Edit</th>
<th scope="col">Delete</th>
</tr>
</thead>
<tbody>
{this.state.hasps.map((hasp, i) =>
<TableHasp
delEvent={(hasp) => this.deleteCurrentHaspInfo(hasp)}
modifyEvent={(id) => this.modifyCurrentHaspInfo(id)}
key={i}
hasps={hasp}
/>)}
</tbody>
</table>
</div>
Function that will modify item data:
modifyCurrentHaspInfo = (hasp) => {
this.setState({
isInEditMode: !this.state.isInEditMode,
editHasp: hasp
})
console.log(hasp);
// if (prompt("Enter password:") === "123456") {
axios.put("/hasp/change",
{
_id: hasp._id,
serial: "yyyyy-99000", //test data
soft: "test-put3" //test data
}
)
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
})
// } else {
// alert("Wrong password!");
// }
}
Send the index instead of the object
<td><i onClick={() => this.props.modifyEvent(i)} className="far fa-edit btnEdit"></i></td>
Modify the value, and add the prop in item.
modifyCurrentHaspInfo = (index) => {
const hasps = [...this.state.hasps];
hasps[index].isInEditMode = true;
this.setState({hasps});
console.log(hasp);
// if (prompt("Enter password:") === "123456") {
axios.put("/hasp/change",
{
_id: hasp._id,
serial: "yyyyy-99000", //test data
soft: "test-put3" //test data
}
)
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
})
// } else {
// alert("Wrong password!");
// }
}
Create a new with the condition.
{this.props.hasps.isInEditMode && <td>ACTION!!!</td>}
IMPORTANT
Do not use INDEX as a key
{this.state.hasps.map((hasp, i) =>
<TableHasp
delEvent={(hasp) => this.deleteCurrentHaspInfo(hasp)}
modifyEvent={(hasps) => this.modifyCurrentHaspInfo(hasps)}
key={i}
hasps={hasp}
/>
)}
Use a unique value from the object.
Why you should not use index as a key.
A key is the only thing React uses to identify DOM elements. What happens if you push an item to the list or remove something in the middle? If the key is same as before React assumes that the DOM element represents the same component as before. But that is no longer true.
Source: https://medium.com/#robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318
I am more familiar with NodeJs than react. I have build a react component that searches for user input and provides the output in a table format based on the value that the user has typed into the search input form. This is working as I want and the code for the module is below:
import React, { Component } from 'react';
import axios from 'axios';
import Suggestions from './Suggestions';
// API url
const API_URL = 'http://localhost:3000/api/file_infos'
class Search extends Component {
state = {
query: '',
results: []
}
getCount = () => {
axios.get(`${API_URL}count?filter[where][id][regexp]=/${this.state.query}/i`)
.then(count => {
this.setState({
results: count.data
})
})
}
// query loop back API for matching queries base on text input
getInfo = () => {
axios.get(`${API_URL}?filter[where][id][regexp]=/${this.state.query}/i&filter[limit]=20`)
.then(response => {
this.setState({
results: response.data
})
})
}
// check to see if input on the search bar has changed and update the search query accordingly
handleInputChange = () => {
this.setState({
query: this.search.value
}, () => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query) {
this.getInfo()
}
} else if (!this.state.query) {
}
})
}
// render form and pass results back to the home component
render() {
return (
<div>
<form>
<input
placeholder="Search for..."
ref={input => this.search = input}
onChange={this.handleInputChange}
/>
</form>
<Suggestions results={this.state.results} />
</div>
)
}
}
export default Search
The second module is the suggestions module that displays the output in the table format.
The next portion of the app I am building will open a file based on the table row that the user selected. I want that table data returned to a function so that I can make an http post request to my API that will in turn open the file using a NodeJS module.
I want the suggestions component to return the value of the data items in the table cells so that the data can be used to send to the API in order to open my files. The code I have come up with so far is only returning an undefined error.
Below is what I currently have:
import React from 'react';
// return results in a table format based on the text input entered
const Suggestions = (props) => {
const state = {
results: []
}
const handleFormOpen = () => {
this.setState({
results: this.results.value
},
console.log(this.state.results)
)
}
const options = props.results.map(r => (
<tr key={r.id} ref={tr => this.results = tr} onClick={handleFormOpen.bind(this)}>
<td>{r.id}</td>
<td>{r.OriginalPath}</td>
<td>{r.CreateDate}</td>
<td>{r.AccessDate}</td>
<td>{r.WriteDate}</td>
<td><i className="fas fa-book-open"></i></td>
</tr>
))
return <table className="striped responsive-table">
<thead>
<tr>
<th>File Name</th>
<th>Parent Directory</th>
<th>Creation Date</th>
<th>Access Date</th>
<th>Write Date</th>
<th>Open File</th>
</tr>
</thead>
<tbody>
{options}
</tbody>
</table>
}
export default Suggestions;
I am really unsure at this point if I am trying to tackle this issue in the correct way. I am thinking that maybe the suggestions component may need to be turned into a full class extending component but I am fairly lost at this point. Can someone please kindly point out my folly and get me going in the right direction?
UPDATE
As requested in the comments here is the error log from my browser:
Suggestions.js:10 Uncaught TypeError: Cannot read property 'results' of undefined
at Object.handleFormOpen (Suggestions.js:10)
at HTMLUnknownElement.callCallback (react-dom.development.js:145)
at Object.invokeGuardedCallbackDev (react-dom.development.js:195)
at invokeGuardedCallback (react-dom.development.js:248)
at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:262)
at executeDispatch (react-dom.development.js:593)
at executeDispatchesInOrder (react-dom.development.js:615)
at executeDispatchesAndRelease (react-dom.development.js:713)
at executeDispatchesAndReleaseTopLevel (react-dom.development.js:724)
at forEachAccumulated (react-dom.development.js:694)
at runEventsInBatch (react-dom.development.js:855)
at runExtractedEventsInBatch (react-dom.development.js:864)
at handleTopLevel (react-dom.development.js:4857)
at batchedUpdates$1 (react-dom.development.js:17498)
at batchedUpdates (react-dom.development.js:2189)
at dispatchEvent (react-dom.development.js:4936)
at interactiveUpdates$1 (react-dom.development.js:17553)
at interactiveUpdates (react-dom.development.js:2208)
at dispatchInteractiveEvent (react-dom.development.js:4913)
First thing Since your Suggestions component plays with state, I would recommend you to go with statefull component.
Stateless component is meant for getting props and returning jsx elements, there wont be any state mutations in stateless component. This is called pure function in javascript. Hope this makes clear.
Also since you declared handleFormOpen as an arrow function you no need to do binding. binding takes care automatically by arrow function. If you don't want to use arrow function and you want to bind it then do the binding always in constructor only but don't do binding anywhere in the component like you did in map.
PFB corrected Suggestions component code
import React, { Component } from 'react';
// return results in a table format based on the text input entered
export default class Suggestions extends Component {
constructor(props){
super(props);
this.state = {
results: [],
value: ""
}
}
handleFormOpen = (path, id) => {
console.log("id", id, path);//like wise pass value to this function in .map and get the value here
this.setState({
value: id
});
}
render(){
const { results } = this.props;
return (<div>
<table className="striped responsive-table">
<thead>
<tr>
<th>File Name</th>
<th>Parent Directory</th>
<th>Creation Date</th>
<th>Access Date</th>
<th>Write Date</th>
<th>Open File</th>
</tr>
</thead>
<tbody>
{Array.isArray(results) && results.length > 0 && results.map(r => (
<tr key={r.id} ref={tr => this.results = tr} onClick={() => this.handleFormOpen(r.OriginalPath, r.id)}>
<td>{r.id}</td>
<td>{r.OriginalPath}</td>
<td>{r.CreateDate}</td>
<td>{r.AccessDate}</td>
<td>{r.WriteDate}</td>
<td><i className="fas fa-book-open"></i></td>
</tr>
))}
</tbody>
</table>
</div>)
}
}
export default Suggestions;
You are using states in Functional Component, You need to use React Component
import React from 'react';
// return results in a table format based on the text input entered
class Suggestions extends React.Component {
constructor(props) {
super(props);
this.state = {
results: [],
}
}
handleFormOpen = () => {
this.setState({
results: this.results.value
},
console.log(this.state.results)
)
}
render () {
const options = this.props.results.map(r => (
<tr key={r.id} ref={tr => this.results = tr} onClick={handleFormOpen.bind(this)}>
<td>{r.id}</td>
<td>{r.OriginalPath}</td>
<td>{r.CreateDate}</td>
<td>{r.AccessDate}</td>
<td>{r.WriteDate}</td>
<td><i className="fas fa-book-open"></i></td>
</tr>
))
return (
<table className="striped responsive-table">
<thead>
<tr>
<th>File Name</th>
<th>Parent Directory</th>
<th>Creation Date</th>
<th>Access Date</th>
<th>Write Date</th>
<th>Open File</th>
</tr>
</thead>
<tbody>
{options}
</tbody>
</table>
)
}
}
export default Suggestions;
I have a view with a table of products that can be added to a shopping cart. Each row has a DropDownList with allowed quantities that can be ordered along with a button to add to cart. Everything is populating and displaying properly. I know how to pass the item ID in the ActionLink but how can I get the value of the DownDownList associated with the table row of the ActionLink that was clicked?
I am guessing possibly using JQuery that fires when the ActionLink is clicked?
I also thought of making every row a form but that seems overkill.
Is there an easy MVC way to do this?
In prepping more info for a proper question and went ahead and solved it. Thank you Stephen for the nudge and info.
I tried putting a Html.BeginForm around each <tr> tag in the details section. This did indeed work for me. I was able to easily get the unique form info to POST for each individual row. However, when I would enable JQuery DataTables the submit would break. DataTables must be capturing the submit or click somehow. Haven't figured that out but it made me try JQuery which seems a much better way to do it.
Here is how I construct the table data row:
#foreach (var item in Model)
{
<tr>
<td>
<img src="#item.GetFrontImage()" width="100" />
</td>
<td>
<strong>#Html.DisplayFor(modelItem => item.DisplayName)</strong>
</td>
<td>
#Html.DisplayFor(modelItem => item.CustomerSKU)
</td>
<td>
#Html.DropDownList("OrderQty", item.GetAllowedOrderQuantities(), htmlAttributes: new { #class = "form-control" })
</td>
<td>
<a class="btn btn-default pull-right" data-id="#item.ID">Add to Cart</a>
</td>
</tr>
}
This creates a select with id of OrderQty and I embedded the item ID in data-id attribute of the link. I then used this JQuery to capture the info and POST it to my controller. Just have a test div displaying the results in this example:
// Add to Cart click
$('table .btn').click(function () {
// Gather data for post
var dataAddToCard = {
ID: $(this).data('id'), // Get data-id attribute (Item ID)
Quantity: $(this).parent().parent().find('select').val() // Get selected value of dropdown in same row as button that was clicked
}
// POST data to controller
$.ajax({
url: '#Url.Action("AddToCart","Shopping")',
type: 'POST',
data: JSON.stringify(dataAddToCard),
contentType: 'application/json',
success: function (data) { $('#Result').html(data.ID + ' ' + data.Quantity); }
})
});
The JQuery function receives the reference to the link being clicked so I can extract the Item ID from the data-id attribute. I can then get a reference to the dropdown (select) that is in the same row by using .parent.parent (gets me to the <tr> tag) and then just finding the next 'select' tag. Probably pretty obvious to a lot of you.
This works great for my purposes. I can also update other elements with data returned from the POST.
Thank you
Karl
for the table in html:
<div class="table-responsive">
<table id="employeeTable"class="table table-bordered">
<thead>
<tr>
<th class="text-center">ُُُEmpId</th>
<th class="text-center">Name</th>
<th class="text-center">Absense State</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>#item.Id</td>
<td>#item.Name</td>
<td class="text-center">#Html.DropDownList("DDL_AbsentStatus", new SelectList(ViewBag.statusList, "Id", "Name"), new { #class = "form-control text-center" })</td>
</tr>
}
</tbody>
</table>
</div>
in javascript to get the selected value:
//Collect Date For Pass To Controller
$("#btn_save").click(function (e) {
e.preventDefault();
if ($.trim($("#datepicker1").val()) == "") {
alert("ادخل تاريخ يوم صحيح!")
return;
}
var employeesArr = [];
employeesArr.length = 0;
$.each($("#employeeTable tbody tr"), function () {
employeesArr.push({
EmpId: $(this).find('td:eq(0)').html(),
EntryDate: $.trim($("#datepicker1").val()),
StatusId: $(this).find('#DDL_AbsentStatus').val()
});
});
$.ajax({
url: '/Home/SaveAbsentState',
type: "POST",
dataType: "json",
data: JSON.stringify(employeesArr),
contentType: 'application/json; charset=utf-8',
success: function (result) {
alert(result);
emptyItems();
},
error: function (err) {
alert(err.statusText);
}
});
})
I have been trying to make this work for several days now. I cannot access a profile object from its associated user object. I have the latest versions of Ember and ember-data
Ember-data: 1.0.0-beta15
Ember: 1.10.0 production
I have a simple table view that lists my users and a couple properties. Here is the view:
<script type="text/x-handlebars" data-template-name="users/verification-candidates">
<div class="small-12 column">
<h1>Candidates for Verification</h>
<table>
<tr>
<th>System ID</th>
<th>Email</th>
<th>Created At</th>
<th>Legal First Name</th>
<th>Legal Last Name</th>
<th></th>
<th></th>
</tr>
{{#each user in model itemController="candidate" }}
<tr>
<td> {{ user.id }}</td>
<td> {{ user.email }}</td>
<td>{{ user.created_at }}</td>
<td>{{ user.legal_first_name }}</td>
<td>{{ user.legal_last_name }}</td>
<td><button id="verify" {{ action "markVerified" }}>Verify</button></td>
<td><button id="disable" {{ action "markDisabled" }}>Disable</button></td>
</tr>
{{/each}}
</table>
</div>
</script>
The models are like so:
App.Profile = DS.Model.extend({
user: DS.belongsTo('user'),
incognito_name : DS.attr('string'),
advisor_id : DS.attr('number'),
created_at : DS.attr('date'),
//etc..
App.User = DS.Model.extend({
profile: DS.belongsTo('profile',{ async: true }),
email: DS.attr('string'),
sign_in_count: DS.attr('number'),
last_sign_in_at: DS.attr('date'),
//etc...
I am using the rest adapter:
App.ApplicationAdapter = DS.RESTAdapter.extend({
host: 'http://localhost:1337',
defaultSerializer: 'json'
});
Pertinent routes:
App.Router.map(function(){
this.resource('users', { path: '/users'}, function(){
this.route('verification-candidates');
});
this.resource('profiles', { path: '/profiles' }, function(){
})
});
App.UsersRoute = Ember.Route.extend({
model: function(){
return this.store.find('user');
}
});
App.ProfilesRoute = Ember.Route.extend({
model: function(){
return this.store.find('profile');
}
})
App.UsersVerificationCandidatesRoute = Ember.Route.extend({
model : function(){
var items = this.store.find('user', { role: "advisor", disabled: false, is_verified: false });
return items;
},
})
My server is a sails.js back end, which accesses a database created by a Rails application.
I want to alter the profile in this object controller, but cannot access it in any meaningful form:
App.CandidateController = Ember.ObjectController.extend({
actions: {
markVerified: function(){
var user = this.get('model');
var profile = user.get('profile');
console.log(profile); //output 1
console.log(profile.incognito_name); //output 2
}
}
});
The output2 is undefined. Output 1 gives me some sort of object with properties __nextSuper, __ember_meta, a bunch of other things, isFulfilled, and content. But, no object properties from the model definition. This appears to be a promisearray,but, I thought this was the way to get a related object. Meanwhile, when I try to treat it as a PromiseArray, per the documentation, i get null, like this:
App.CandidateController = Ember.ObjectController.extend({
actions: {
markVerified: function(){
var user = this.get('model');
user.get('profile').then(function(content){
console.log("promise content is " + content);
//prints 'promise content is null'
})
//console.log(profile);
//console.log(profile.incognito_name);
}
}
I am fairly certain all my back end server/client things are in order, as I can access the user objects and work with them on the page. I am thinking it may be related to how the profile relates to the user via advisor_id, but, I am so confused right now. I am very much at my wit's end.