I am working on a simple restaurant web app which uses a mongo-db database to store the menu items. My issue is that I have a client js file that will use a routing function that then accesses the database to return all the menu items of a certain restaurant. My issue is that my endpoint for the url isn't being recognized:
Client.js
function readMenu(rest){
(async () => {
// const newURL = url + "/menus/"+rest
const resp = await fetch(url+"/menus/"+rest)
const j = await resp.json();
itemlist = j["items"]
var element = document.getElementById("menu")
var i;
for (i = 0; i < itemlist.length; i++) {
var para = document.createElement("p")
item = itemList[i]
text = item["name"]+" | "+item["cost"]+" | "+item["descr"] +"<br>";
var node = document.createTextNode(text)
para.appendChild(node)
element.appendChild(para)
}
})
}
Server-routing.ts (Routings):
this.router.get("/menus", this.getResturants.bind(this))
this.router.post("/menus", this.addResturaunt.bind(this))
this.router.get("/menus/:rest", this.getResturauntItems.bind(this))
this.router.delete("/menus/:rest",this.deleteResturaunt.bind(this))
this.router.get("/menus/:rest/:item",[this.errorHandler.bind(this),this.getItem.bind(this)])
this.router.post("/menus/:rest",this.addItem.bind(this))
this.router.delete("/menus/:rest/:item",this.deleteItem.bind(this))
Server-routing.ts (function):
public async getResturauntItems(request, response) : Promise<void> {
console.log("Getting Restaurant Items")
let rest = request.params.rest
let obj = await this.theDatabase.getResturauntItems(rest)
console.log(obj)
response.status(201).send(JSON.stringify(obj))
response.end()
}
So, what should happen is a button calls readMenu(), it then makes a GET fetch request to localhost:8080/api/menus/ and then the menu items from the collection should be returned. The issue is that when I click the button, nothing happens. I know it is not being redirected to some other function as they all have "console.log()" to keep track of them and none of them where called. I used the "inspect" tool to see if the request was being sent or received anywhere and nothing. I am unsure of what the issue happens to be. If anyone can help, it would be really appreciated.
you just never called your function, you declared the async function inside your function but never called it.
function readMenu(rest){
(async () => {
// const newURL = url + "/menus/"+rest
const resp = await fetch(url+"/menus/"+rest)
const j = await resp.json();
itemlist = j["items"]
var element = document.getElementById("menu")
var i;
for (i = 0; i < itemlist.length; i++) {
var para = document.createElement("p")
item = itemList[i]
text = item["name"]+" | "+item["cost"]+" | "+item["descr"] +"<br>";
var node = document.createTextNode(text)
para.appendChild(node)
element.appendChild(para)
}
})();
}
you need to add () after creating the functions to call it.
Related
I'm a new developer and am currently asked to set up a login / signup page for a Single Page Application in JavaScript. This SPA is in the early stages, since the published version is a multipage application.
Our database already contains the user information. I suppose I would have to work with post and get requests in order to authenticate users?
So far, I have managed to fetch data from the database in json, but I don't know how to handle user input and send it to the database.
A general overview: Our Single Page Application is written in JavaScript. A separate Python file retrieves data from the database, creates specific routes (urls) (which we access in our js files) and stores the data in json.
P.S. the code might be pretty janky, and any suggestions on how to improve or simplify the code are appreciated
rest_server.py:
#app.route(url_prefix + '/json/team/<int:team_id>')
def get_team(team_id):
team = adapter.team_info(team_id)
resp = flask.jsonify(team)
resp.headers['Access-Control-Allow-Origin'] = '*'
return resp
#app.route(url_prefix + '/json/team_ids')
def get_team_ids():
team_ids = adapter.team_ids()
resp = flask.jsonify(team_ids)
resp.headers['Access-Control-Allow-Origin'] = '*'
return resp
main.js (reduced version here, I hope it makes sense):
async function renderPage(path) {
if (!path) {
path = window.location.pathname;
}
console.log(`render page ${path}`);
let renderedPath = await pathHandler.createContent(path);
if (renderedPath && renderedPath != path) {
// if page was rendered under a different name, override history
console.log(`different rendered path ${renderedPath}`);
window.history.replaceState(renderedPath,'', renderedPath);
}
}
/**
* handles back button events on window object
* #param {Event} event event object emitted by window.
*/
function onBackButton(event) {
event.preventDefault();
renderPage();
}
/**
* handles click events on the document interface
* #param {Event} event object on the document interface
*/
function onBodyClick(event) {
var tag = event.target;
// use default behavior if this was not a hyperlink
if (tag.tagName != 'A' || !tag.href || event.button != 0) {
return;
}
// use default behavior if link points to other server
if (tag.origin != window.location.origin) {
return;
}
// use default behavior if path is not supported
if (!pathHandler.isSupported(tag.pathname)) {
return;
}
event.preventDefault();
redirect(tag.pathname);
}
/**
* determines the path prefix, the page is loaded with. at the moment, a path prefix requires an
* indicator string. in future, the path could be transmitted within the HTML page and extracted here
* #returns {String|null} path prefix or null if there is none.
*/
function determinePathPrefix() {
let pathPrefixIndicator = '/sp/';
let path = window.location.pathname;
let index = path.lastIndexOf(pathPrefixIndicator);
if (index >= 0) {
return path.slice(0, index + pathPrefixIndicator.length - 1);
}
return null;
}
function displayTeams() {
contentCreator.createDisplayTeams();
}
const contentElement = document.querySelector('.content');
const headerElement = document.querySelector('.details');
const teamsLi = document.querySelector('#teams');
const baseElements = {
headerElement: headerElement,
contentElement: contentElement
};
const functions = {
displayTeams: displayTeams
}
const pathPrefix = determinePathPrefix();
console.log(`path prefix: ${pathPrefix}`);
const pathHandler = new PathHandler(pathPrefix);
const contentCreator = new ContentCreator(pathHandler, baseElements, functions);
const createDisplayTeams = contentCreator.createDisplayTeams.bind(contentCreator);
const createTeams = contentCreator.createTeams.bind(contentCreator);
pathHandler.addPath('createTeams', '/team_<int:itemId>', createTeams);
pathHandler.addPath('teams', '/teams', createDisplayTeams);
document.body.addEventListener('click', onBodyClick);
teamsLi.addEventListener('click', displayTeams);
window.addEventListener('popstate', onBackButton);
renderPage();
Then there is a PathHandler.js file, which might not be necessary to show here.
Finally, ContentCreator.js, which takes care of the UI (also reduced):
class ContentCreator {
#pathHandler;
#baseElements;
#functions;
/**
* #param {PathHandler} pathHandler path handler to be used for generating links.
* #param {Object} baseElements base HTML elements of which children can be replaced.
* #param {Object} functions functions for reloading or redirecting.
*/
constructor(pathHandler, baseElements, functions) {
this.#pathHandler = pathHandler;
this.#baseElements = baseElements;
this.#functions = functions;
}
// the displayTeams function in main.js calls this function
async createDisplayTeams() {
let paragraphElement = document.createElement('p');
paragraphElement.setAttribute('class', 'teamsList');
const response = await fetch('http://localhost:5000/json/teams');
const teams = await response.json();
for (let i = 0; i < teams.length; i++) {
const id = teams[i].TeamId;
const li = document.createElement('li');
const link = document.createElement('a');
const name = teams[i].Name;
link.innerHTML = name;
li.appendChild(link);
li.setAttribute('class', 'teamLi');
paragraphElement.appendChild(li);
link.setAttribute('href', this.#pathHandler.generatePath('createTeams', {itemId: id}));
}
this.#baseElements.contentElement.replaceChildren(paragraphElement);
}
async createTeams(args) {
const id = args['itemId'];
const infoResponse = await fetch(`http://localhost:5000/json/team/${id}`);
const teamInfo = await infoResponse.json();
const roleInfo = teamInfo['RoleInfoList'];
const members = document.createElement('ul');
const container = document.createElement('div');
container.setAttribute('class', 'teamContainer');
const header = document.createElement('h2');
header.innerHTML = teamInfo['Name'];
container.appendChild(header);
const paragraphElement = document.createElement('p');
if (teamInfo['Email']) {
paragraphElement.innerHTML = teamInfo['Email'];
}
for (let p = 0; p < roleInfo.length; p++) {
const member = document.createElement('li');
member.innerHTML = `${roleInfo[p]['Person Name']}`;
members.appendChild(member);
}
container.appendChild(paragraphElement);
container.appendChild(members);
this.#baseElements.contentElement.replaceChildren(container);
}
}
Now I would like to create a form in ContentCreator.js, which somehow fetches the user input and then makes a database request in order to authenticate the user. The UI code won't be a problem, but how could I fetch and send the data?
Thank you!!
Mina
I've read multiple posts on here about reading in table data, however, every post is how to read and use that data within a single test function. How do I read a table in, and use it in any test function within a test spec?
If I write this into a test function it works and gives back all the info correctly:
import { sAdmin } from "..authentication";
import TablePage from "../TablePage";
const tablePage = new TablePage();
fixture `A fixture`
.page`https://subdomain.example.com/#/table`
.beforeEach( async t => {
await t
.resizeWindow(1284, 722)
.useRole(sAdmin);
});
// this will work, but this won't share context with any other test function in the spec
// this should be initialized at the start to be able to verify search clears
test(`A test`, async t => {
const tableColumn = await tablePage.woNumber;
const tableCount = await tablePage.woNumber.count;
const cellData = [];
for (let i = 0; i < tableCount; i++) {
const text = await tableColumn.nth(i).innerText;
cellData.push(text);
}
console.log('in spec - tableCount', tableCount);
console.log('in spec - cellData', cellData);
});
The output of both console logs is correct:
in spec - tableCount 4
in spec - cellData [ '0007', '0003', '0005', '0006' ]
I've tried an async function in my test spec, and in my page object model (POM). Async function won't work in my spec unless it's within a test function. The one in the POM works, it'll get called, however i can't do const tableCount = await tablePage.woNumber.count; it will yell at me that I can't use a selector like that. It's because of the modifier .count. I adjusted the .count to be within the for loop but that just returned undefined or other data that didn't help.
Example of the async function in my page object model (TablePage)
async rowCount() {
const tableColumn = await tablePage.fooSelector;
const tableCount = await tablePage.fooSelector;
const cellData = [];
for (let i = 0; i < tableCount.count; i++) {
const text = await tableColumn.nth(i).innerText;
cellData.push(text);
}
console.log('in page - tableColumn', tableColumn);
console.log('in page - tableCount', tableCount);
console.log('in page - cellData', cellData);
return tableCount;
};
It's called with this in my spec file, not sure where to call it though:
const count = tablePage.rowCount();
I need this to run after the page has loaded to grab the cell data, and allow me to share context across all tests within this spec file at the very minimum. I'd prefer to put it in my POM, so it can be used elsewhere in other tests. But I'd settle for it working in my test spec without it being in a test function so it can be shared across all tests in the spec file.
I've tried to do a fixture context, but that also had issues and returned undefined. Here is a before with context that I tried, that didn't work.
.before( async ctx => {
const tableColumn = await tablePage.fooSelector;
const tableCount = await tablePage.fooSelector;
for (let i = 0; i < tableCount.count; i++) {
const text = await tableColumn.nth(i).innerText;
ctx.cellData.push(text);
}
// console.log('in spec - tableCount', tableCount);
// console.log('in in spec - cellData', cellData);
})
These console logs return undefined, or objects instead of the text.
Any help would be greatly appreciated. Here are resources I referenced already:
TestCafe - Storing results of Selector in variable
How do I can get a text of all the cells of the table using testcafe
Testcafe get text from element
https://testcafe.io/documentation/402670/reference/test-api/domnodestate
EDIT: I'm still looking for a way to share context of the data I get, I wasn't able to return the data back to the test spec. Maybe if I do more tinkering I can share the values I've obtained.
Here is my solution that doesn't share context, but it does let me prevent code reuse in every test function.
Page Object Model
import { Selector, t } from "testcafe";
class TablePage{
constructor() {
this.searchInput = Selector('#searchInput');
this.tableCount = Selector('.class-selector');
};
async validateSearchResults(selector, searchText) {
await t
.typeText(this.searchInput, searchText)
.pressKey('enter');
const rowCount = await this.tableCount.count;
let searchResults = []
for (let i = 0; i < rowCount; i++) {
let text = await selector.nth(i).innerText;
searchResults.push(text);
await t.expect(searchResults[i]).contains(searchText);
}
};
}
export default TablePage;
Spec File
import { sAdmin } from "..authentication";
import TablePage from "../TablePage";
const tablePage = new TablePage();
fixture `Test search functionality`
.page`https://examplepage.com`
.beforeEach( async t => {
await t
.useRole(sAdmin)
});
test(`User can search via order number`, async t => {
await tablePage.validateSearchResults(tablePage.tableCount, 'foo');
});
The const tableCount = await tablePage.selector.count; looks correct and should work. Also, it's necessary to call the async method with await keyword:
const count = await tablePage.rowCount();
Here is an example of a similar approach:
table-page.js:
import { Selector } from 'testcafe';
export class TablePage {
constructor () {
this.tableCells = Selector('#ContentHolder_grid_DXDataRow0 td');
this.cellData = [];
this.cellCount = 0;
}
async initCellData () {
this.cellCount = await this.tableCells.count;
for (let i = 0; i < this.cellCount; i++) {
const text = await this.tableCells.nth(i).innerText;
this.cellData.push(text);
}
}
}
test.js:
import { TablePage } from './table-page';
let page = null;
fixture `New Fixture`
.page `https://demos.devexpress.com/ASPxGridViewDemos/DataBinding/QueryBuilderControl.aspx`
.beforeEach(async () => {
page = new TablePage();
await page.initCellData();
});
test(`New Test`, async t => {
console.log('cells count: ', page.cellCount);
console.log('cells data: ', page.cellData);
});
I'm using parse with javascript (vueJs) and i'm experiencing the following issue : when I destroy an object and then get all my data, the object is still in the result. However, if I look in my parse backend, the object has been deleted.
This is my code:
methods: {
getNotes: async function() {
console.log("\nGETTING ALL NOTES")
const Object = Parse.Object.extend("notes");
const query = new Parse.Query(Object);
query.equalTo("user", Parse.User.current());
query.descending("createdAt");
const results = await query.find();
for (let i = 0; i < results.length; i++) {
var temp = {}
const object = results[i];
temp.id = object.id
temp.note = object.get('note')
temp.date = object.get('createdAt')
this.notes.push(temp)
}
},
deleteNote: async function() {
const Object = Parse.Object.extend("notes");
const query = new Parse.Query(Object);
query.equalTo("objectId", this.selectedItem);
const results = await query.first();
if (results){
await results.destroy() // I wait for the object to be destroyed
await this.getNotes() //Then, I call (again) my function to get all notes ... but it still contains the deleted object !!
}else{
alert("There was a problem with the query")
}
}
}
Any ideas why this happens and how can I "check" when my parse database is up to date so that I can get the data again (without the removed object) ?
I have created an async function that will extra the data from the argument, create a Postgres query based on a data, then did some processing using the retrieved query data. Yet, when I call this function inside a map function, it seemed like it has looped through all the element to extra the data from the argument first before it proceed to the second and the third part, which lead to wrong computation on the second element and onwards(the first element is always correct). I am new to async function, can someone please take at the below code? Thanks!
async function testWeightedScore(test, examData) {
var grade = [];
const testID = examData[test.name];
console.log(testID);
var res = await DefaultPostgresPool().query(
//postgres query based on the score constant
);
var result = res.rows;
for (var i = 0; i < result.length; i++) {
const score = result[i].score;
var weightScore = score * 20;
//more computation
const mid = { "testID": testID, "score": weightScore, more values...};
grade.push(mid);
}
return grade;
}
(async () => {
const examSession = [{"name": "Sally"},{"name": "Bob"},{"name": "Steph"}]
const examData = {
"Sally": 384258,
"Bob": 718239,
"Steph": 349285,
};
var test = [];
examSession.map(async sesion => {
var result = await testWeightedScore(sesion,examData);
let counts = result.reduce((prev, curr) => {
let count = prev.get(curr.testID) || 0;
prev.set(curr.testID, curr.score + count);
return prev;
}, new Map());
let reducedObjArr = [...counts].map(([testID, score]) => {
return {testID, score}
})
console.info(reducedObjArr);
}
);
})();
// The console log printed out all the tokenID first(loop through all the element in examSession ), before it printed out reducedObjArr for each element
The async/await behaviour is that the code pause at await, and do something else (async) until the result of await is provided.
So your code will launch a testWeightedScore, leave at the postgresql query (second await) and in the meantime go to the other entries in your map, log the id, then leave again at the query level.
I didn't read your function in detail however so I am unsure if your function is properly isolated or the order and completion of each call is important.
If you want each test to be fully done one after the other and not in 'parallel', you should do a for loop instead of a map.
I'm using phantom 6.0.3 to scrape a web page. Here is the initial setup:
(async function () {
const instance = await phantom.create(['--ignore-ssl-errors=yes', '--load-images=no', '--web-security=false'], {logLevel: 'error'});
const page = await instance.createPage();
await page.on('onResourceRequested', function (requestData) {
console.info('Requesting', requestData.url);
});
const url = // Some url
const status = await page.open(url);
const content = await page.evaluate(function () {
return document.querySelector('ul > li');
});
const contentLength = content.length // 5
//Code Block 2 goes here
})();
So far everything works fine. It was able to successfully determine that the length of the content is 5 (there are 5 li items). So what I want to do now is get the innerText of each of those li elements... and this is where I get my issue.
I've try using a for loop to retrieve the innerText of each li element, but it always returns null. Here's what I've tried:
//Code Block 2:
for (let i = 0; i < contentLength; i++) {
const info = await page.evaluate(function () {
const element = document.querySelector('ul > li');
return element[i].innerText;
});
console.log(info); // this returns null 5 times
}
I don't know what's going on. I can give a specific index to return, such as: return element[3].innerText, and this will give me the correct innerText, but I can't get this working via loop
PhantomJS evaluates the function in a different context so it's not aware of the parameter i.
You should pass i to the evaluate function in order to forward it to the browser process:
for (let i = 0; i < contentLength; i++) {
const info = await page.evaluate(function (index) { // notice index argument
const element = document.querySelector('ul > li');
return element[index].innerText;
}, i); // notice second argument is i
console.log(info);
}