I am using chai and javascript to run some automated tests against API endpoints.
In one of my tests, I am pushing results into an array (shown below, the array is named acceptanceCriteriaHashes).
If I put a breakpoint in the before code then I can see the array, and all the elements within the array, are fully populated with the object returned from the db call.
However when I actually come to verify the contents of the array later in the test, the array, although it has the correct number of elements, contains only undefined?
Have tried to push directly with the results of the db call and that doesn't work either?
const getAcceptanceCriteria = (testCase) => {
let getAcceptanceCriteriaResponse;
let myLicenceChecksPassed = false;
const acceptanceCriteriaHashes = new Array();
describeTest(`${testCase.testCaseId} - ${testCase.testDescription}`, async () => {
before(async () => {
if (testCase.myLicenceChecksPassed !== undefined) {
myLicenceChecksPassed = testCase.myLicenceChecksPassed;
}
getAcceptanceCriteriaResponse = await getAcceptanceCriteriaAPICall(testCase.productId, testCase.assetType, testCase.underwriter, myLicenceChecksPassed);
if (getAcceptanceCriteriaResponse != undefined && getAcceptanceCriteriaResponse.body.Detail.length > 0) {
for (let i = 0; i < getAcceptanceCriteriaResponse.body.Detail.length; i++) {
// eslint-disable-next-line prefer-const
let response = await readAcceptanceCriteriaHashes(getAcceptanceCriteriaResponse.body.Detail[i].ContentHash);
if (response != undefined) {
let record = { Hash: response.Hash, Criteria: response.Criteria };
acceptanceCriteriaHashes.push(record);
}
}
console.log(acceptanceCriteriaHashes);
}
});
// other tests successfully run here
it(`${testCase.testCaseId} - Record for contentHash exists in the AcceptanceCriteriaHashes table`, async () => {
expect(acceptanceCriteriaHashes).to.not.be.empty;
expect(getAcceptanceCriteriaResponse.body.Detail.length).to.eql(acceptanceCriteriaHashes.length);
for (let i = 0; i < acceptanceCriteriaHashes.length; i++) {
expect(acceptanceCriteriaHashes[i].to.not.be('undefined', `Returned undefined for ContentHash: ${getAcceptanceCriteriaResponse.body.Detail[i].ContentHash}`));
expect(acceptanceCriteriaHashes[i].Criteria.to.not.be.empty);
}
});
});
};
The issue was I needed to declare the record variable outside of the before code block otherwise, when we exited the before function, it ceased to exist by the time I came to query it in the subsequent test..
Declaration goes just before the before function:
const getAcceptanceCriteria = (testCase) => {
let getAcceptanceCriteriaResponse;
let myLicenceChecksPassed = false;
// eslint-disable-next-line prefer-const
// eslint-disable-next-line no-array-constructor
const acceptanceCriteriaHashes = new Array();
let record;
describeTest(`${testCase.testCaseId} - ${testCase.testDescription}`, async () => {
before(async () => {
if (testCase.myLicenceChecksPassed !== undefined) {
myLicenceChecksPassed = testCase.myLicenceChecksPassed;
}
getAcceptanceCriteriaResponse = await getAcceptanceCriteriaAPICall(testCase.productId, testCase.assetType, testCase.underwriter, myLicenceChecksPassed);
if (getAcceptanceCriteriaResponse != undefined && getAcceptanceCriteriaResponse.body.Detail.length > 0) {
for (let i = 0; i < getAcceptanceCriteriaResponse.body.Detail.length; i++) {
// eslint-disable-next-line prefer-const
let response = await readAcceptanceCriteriaHashes(getAcceptanceCriteriaResponse.body.Detail[i].ContentHash);
if (response != undefined) {
record = { Hash: response.Hash, Criteria: response.Criteria };
acceptanceCriteriaHashes.push(record);
}
}
console.log(acceptanceCriteriaHashes);
}
});
Related
I'm trying to create a chrome extension, but I am having some trouble updating my DB.
In the code below I am using index.get to the the object that contains a certain value. If such an object doesn't exist I will create a new one, which works just fine.
But if the DB contains an object with the specified value, I want to append a new object to an array (allMessages) that is inside the object I searched for. The details doesn't really matter in this case.
What is important is to find out if the way I'm adding this new obj to the array (allMessages) is a valid way of updating the database.
records.forEach((person) => {
console.log("here1");
const index = objectStore.index("urlKeyValue");
let search = index.get(person.urlKeyValue);
search.onsuccess = function (event) {
if (search.result === undefined) {
// no record with that key
let request = objectStore.add(person);
request.onsuccess = function () {
console.log("Added: ", person);
};
} else {
// here I'm iterating an array that is inside the obj I searched for,
// and then checking if the key for that array matches **theUserId**
for (userObj of event.target.result.allMessages) {
if (theUserId == Object.keys(userObj)) {
// is this part correct. Is it possible to update the DB this way?
let objToAdd1 = {
time: person.allMessages[0][theUserId][0].time,
msg: person.allMessages[0][theUserId][0].msg,
};
let currentObj = userObj[theUserId];
let updatedObj = currentObj.push(objToAdd1);
}
}
)}
Using objectStore.openCursor you can update only part of the record.
The following updates only book prices.
const transaction = db.transaction("books", "readwrite");
const objectStore = transaction.objectStore("books");
records = [{ id: "kimetu", price: 600 }];
records.forEach((book) => {
const index = objectStore.index("id");
const search = index.get(book.id);
search.onsuccess = () => {
if (search.result === undefined) {
const request = objectStore.add(book);
request.onsuccess = () => {
console.log("Added: ", book);
};
} else {
const request = objectStore.openCursor(IDBKeyRange.only(book.id));
request.onsuccess = () => {
const cursor = request.result;
if (cursor) {
cursor.value.price = 1000;
const updateRequest = cursor.update(cursor.value);
updateRequest.onsuccess = () => {
console.log("Updated: ", cursor.value.price);
};
cursor.continue();
}
};
}
}
});
i get error
let executor = await this.members.fetch(executorID);
^^^^^
SyntaxError: await is only valid in async function
when using the code below (use is to check if user breaks any of set filters and if so remove roles or ban user whatever they set option to)
ive tried my best to lable what parts of code does please not english isnt my first language
ive only recieved this error since trying to add a check whitelist feature - everything else works without the whitelist check code
without the code for whitelist the code works and performs as intended and the whitelist code succesfully logs ids for that guild
if(whitelisted && whitelisted.length) {
whitelisted.forEach(x => {
if (executorID === x.user) return;
const { Structures } = require('discord.js');
let whitelisted = db.get(`whitelist_${message.guild.id}`)
const { limits, defaultPrefix } = require('../config.js');
Structures.extend('Guild', Guild => {
class GuildExt extends Guild {
constructor(...args) {
super(...args);
}
get prefix() {
return this.get('prefix', defaultPrefix);
}
get(key, fallback) {
return this.client.db.get(`${this.id}_${key}`) || fallback;
}
set(key, data) {
return this.client.db.set(`${this.id}_${key}`, data);
}
delete(key) {
return this.client.db.delete(`${this.id}_${key}`);
}
resolveChannel(channelID) {
const channel = this.channels.cache.get(channelID);
return channel;
}
get limits() {
var obj = {};
for (var k in limits) {
obj[k] = {
minute: this.get(
`limits.${k}.minute`,
limits[k].per_minute
),
hour: this.get(`limits.${k}.hour`, limits[k].per_hour)
};
}
return obj;
}
getActions(limit = 10, filter = () => true) {
var obj = {};
var l = limits;
for (var k in limits) {
obj[k] = {
name: this.client.Utils.toProperCase(k),
actions: this.client.Utils.convertEntries(
[
...this.get(
this.client.Utils.convertLimitNameToActionType(
k
),
[]
),
...this.get(
`archive.${this.client.Utils.convertLimitNameToActionType(
k
)}`,
[]
)
]
.filter(filter)
.slice(0, limit)
)
};
}
return obj;
}
find_entry(action, filter) {
let guild = this;
return new Promise(resolve => {
(async function search(iter) {
//console.log(`ACTION = ${action} | ITER = ${iter}`);
if (!guild.me) return resolve(null);
if (guild.me.hasPermission('VIEW_AUDIT_LOG')) {
let logs = await guild.fetchAuditLogs({
limit: 10,
type: action
});
let entries = logs.entries;
let entry = null;
entries = entries.filter(filter);
for (var e of entries)
if (!entry || e[0] > entry.id) entry = e[1];
if (entry) return resolve(entry);
}
if (++iter === 5) return resolve(null);
else return setTimeout(search, 200, iter);
})(0);
});
}
push_entry(entry, displayName) {
const action = ['MEMBER_KICK', 'MEMBER_BAN_ADD'].includes(
entry.action
)
? 'MEMBER_REMOVE'
: entry.action;
const oneHourAgo = Date.now() - 1000 * 60 * 60;
// Fetch Entries for a sepcific action (Last Hour)
let entries = this.get(action, []);
// Filter entries older than one hour to a new variable
let olderThanOneHour = entries.filter(
i => !(i.timestamp > oneHourAgo)
);
// Prepend entries older than one hour to the archive
if (olderThanOneHour.length > 0)
this.set(`archive.${action}`, [
...olderThanOneHour,
...this.get(`archive.${action}`, [])
]);
// Filter entries older than one hour from old variable
entries = entries.filter(i => i.timestamp > oneHourAgo);
// Prepend new entry if not already found
if (
!entries.find(
i =>
i.target.id === entry.target.id &&
i.executor.id === entry.executor.id
)
)
entries.unshift({
timestamp: entry.createdTimestamp,
action: entry.action,
target: {
id: entry.target.id,
displayName,
targetType: entry.targetType
},
executor: {
id: entry.executor.id,
displayName: entry.executor.tag
}
});
// Update entries newer than one hour
return this.set(action, entries);
}
async check_limits(entries, executorID, configAction) {
// Ignore if executor is the owner or is whitelisted
if (executorID === this.ownerID) return;
if(whitelisted && whitelisted.length) {
whitelisted.forEach(x => {
if (executorID === x.user) retrun;
// Filter actions relating to executor
const oneMinuteAgo = Date.now() - 1000 * 60;
let executorActionsHour = entries.filter(
i => i.executor.id === executorID
);
let executorActionsMinute = executorActionsHour.filter(
i => i.timestamp > oneMinuteAgo
);
console.log(
`${configAction}/${executorID}: LAST_HOUR: ${executorActionsHour.length} LAST_MINUTE: ${executorActionsMinute.length} `
);
let limits = this.limits;
let limitReached = null;
if (executorActionsHour.length >= limits[configAction].hour)
limitReached = 'Hour';
if (executorActionsMinute.length >= limits[configAction].minute)
limitReached = 'Minute';
// Check if the amount of actions is greater than or equal to the limit
if (limitReached) {
// Remove all of the executor's roles
let executor = await this.members.fetch(executorID);
executor.roles.remove(executor.roles.cache);
// Handle managed roles
let managed = executor.roles.cache
.filter(r => r.managed)
.array();
for (var i = 0; i < managed.length; i++)
managed[i].setPermissions(0, 'Guardian Action');
// Notify owner, executor, and logging channel
const embed = this.client.util
.embed()
.setTitle(`Limit Reached - ${limitReached}`)
.setDescription(
this.client.Utils.convertEntries(
limitReached === 'Hour'
? executorActionsHour
: executorActionsMinute
)
)
.setColor(0x7289da);
await this.owner.send(
embed.setFooter(
"This message was sent to you because you're the Guild owner."
)
);
await executor.send(
embed.setFooter(
'This message was sent to you because you were the executor.'
)
);
const loggingChannel = this.resolveChannel(
this.get(`loggingChannelID`)
);
if (loggingChannel)
await loggingChannel.send(embed.setFooter(''));
}
})
}
}
}
return GuildExt;
});
i am new to JS and any help would be appreciated
please dont hate if i do have bad syntax !!
i am new - sorry if i dont get things the first time
You forgot to make your forEach function async, just change it to:
/* ... */
whitelisted.forEach(async (x) => {
/* ... */
let executor = await this.members.fetch(executorID);
/* ... */
}
/* ... */
Not part of your question but you misspelled return here
if (executorID === x.user) retrun;
Your line
let executor = await this.members.fetch(executorID);
is inside a non-async anonymous function in:
if (whitelisted && whitelisted.length) {
whitelisted.forEach(x => { // <- This line
if (executorID === x.user) return;
// ...
Try changing it with:
if (whitelisted && whitelisted.length) {
whitelisted.forEach(async (x) => { // <- Use async here
if (executorID === x.user) return;
// ...
Also, avoid using forEach to make asynchronous calls.
I am doing some practice in node.js. In this exercise I been asked to find a country name through a GET Http Request to an endpoint passing a page integer as a parameter.
Where the important response structs are these {page, total_pages, data}.
page is the current page,
total_pages is the last page,
data is an array of 10 country object.
In getCountryName func I am able to retrieve the right answer only if the answer is on the 1st page, the 1 iteration of the loop. So, why the loop only happens once?
Aditional, I wanted to retrieve the total_pages to replace the hardcode '25' value but I do not figure it out how to return it along with the search.
Any hint you wanna give me? The whole problem is in getCountryCode func.
'use strict';
const { Console } = require('console');
const https = require('https');
function makeRequest(page){
return new Promise(resolve => {
let obj='';
https.get('https://jsonmock.hackerrank.com/api/countries?page='+page, res => {
let data ='';
res.on('data',function(chunk){
data+=chunk;
});
res.on('end',function(){
obj=JSON.parse(data);
resolve(obj);
});
});
});
}
async function getCountryName(code) {
var res = '';
var pages = 25;
var i = 1;
while(i <= pages && res == ''){
console.log(i);
res = makeRequest(i)
.then(data => {
let f = ''
let p = data['total_pages'];
let search = data['data'].find(o => o.alpha3Code === code);
f = search != null ? search['name'] : f;
return f;
});
i++;
}
return res;
}
async function main() {
const name = await getCountryName('ARG');
console.log(`${name}\n`);
}
main();
Without modifying your code too much, this is how you do it:
'use strict';
const { Console } = require('console');
const https = require('https');
function makeRequest(page){
return new Promise(resolve => {
let obj='';
https.get('https://jsonmock.hackerrank.com/api/countries?page='+page, res => {
let data ='';
res.on('data',function(chunk){
data+=chunk;
});
res.on('end',function(){
obj=JSON.parse(data);
resolve(obj);
});
});
});
}
async function getCountryName(code) {
const pages = 25;
var i = 1;
let f = null
while(i <= pages && f === null){
console.log(i);
const data = await makeRequest(i) // put in try/catch
const p = data['total_pages'];
const search = data['data'].find(o => o.alpha3Code === code);
f = search !== null ? search['name'] : null;
i++;
}
return res;
}
async function main() {
const name = await getCountryName('ARG');
console.log(`${name}\n`);
}
main();
tldr at the bottom:
I don't really know how to explain my problem so I start with an example.
I have this async function (in reactJS but I think this is a JS related issue).
onUploadDrop = async (e, folderId) => {
e.preventDefault();
// check if the user uploaded files or folders
var uploadedItems = e.dataTransfer.items;
let files = [];
for (var i = 0; i < uploadedItems.length; i++) {
let item = uploadedItems[i].webkitGetAsEntry();
if (item.isDirectory) {
alert("is directory")
} else {
var file = await this.getFileByWebkitEntry(item);
files.push(file);
}
console.log(i);
}
// do something with files[]
}
This function is calling another async function:
getFileByWebkitEntry = async (item) => {
return new Promise(resolve => {
item.file(function (file) {
resolve(file);
}, function (err) {
console.log(err);
resolve("");
});
});
}
I'm looping through e.datatransfer.files which are basically some uploaded files or folders. Unfortunately this for-loop gets only executed once.
I did some debugging and found out that if I place a console.log before and after this line: var file = await ... This comes out:
tldr: After the await statement uploadedItems is empty thus ending the loop. Why is this happening?
I solved this by not using async - await but Promises instead.
It looks like this:
onUploadDrop = (e, folderId) => {
e.preventDefault();
// check if the user uploaded files or folders
var uploadedItems = e.dataTransfer.items;
let promises = [];
for (var i = 0; i < uploadedItems.length; i++) {
let item = uploadedItems[i].webkitGetAsEntry();
if (item.isDirectory) {
alert("is directory")
} else {
promises.push(this.getFileByWebkitEntry(item));
}
console.log(i);
}
Promise.all(promises).then(result => {
// do something with result (result = files)
});
On my Node JS backend I run this method.
var locations = [];
exports.constructionsiteParser = function constructionsiteParser(response){
var timestamp = new Date().toDateInputValue();
const $ = cheerio.load(response);
$('situation').each( function(){
var situation = [];
$(this).find('situationRecord').each( function(i){
var startLocationCode = $(this).find('alertCMethod2SecondaryPointLocation').find('specificLocation').text();
var endLocationCode = $(this).find('alertCMethod2PrimaryPointLocation').find('specificLocation').text();
var overallStartTime = $(this).find('overallStartTime').text();
var overallEndTime = $(this).find('overallEndTime').text();
if((startLocationCode != '') && new Date(timestamp) >= new Date(overallStartTime) && new Date(timestamp) <= new Date(overallEndTime) ){
Promise.all([
locationCodeToGeodataRequst.geodataByLocationcode(startLocationCode),
locationCodeToGeodataRequst.geodataByLocationcode(endLocationCode)
]).then( values =>{
return createSituationRecord($, this, startLocationCode, endLocationCode, values[0], values[1]);
}).then( function(obj){
console.log("before push", situation);
situation.push(obj);
console.log("after push", situation);
return situation;
}, handleError);
}
})
console.log("from outter", situation.length);
if(situation.length > 0){ //if situation is not empty
locations.push(situation);
}
})
console.log(locations);
}
The console.log("from outter", situation.length); at the bottom prints always 0
also the console.log(locations) is empty
This is a part of the log:
...
from outter 0
from outter 0
from outter 0
from outter 0
from outter 0
[]
before push []
after push [....
I think this happens because the node server runs the bottom part before the inner each loop finishes. So I want to make it more snychronized. What I want to do is something like:
outer each{
//run this first
inner each{
.....
}
//if inner each is done run this
if(...){}
}
But I don't know how to put this in the correct syntax.
I have tried it with nested Promises but it doesn't work.
you can return this promise. deal it at caller
You can make use of async.eachOf(). I took a different approach in making your code synchronous. Hope it helps you.
'use strict';
let locations = [];
exports.constructionsiteParser = function constructionsiteParser(response) {
const $ = cheerio.load(response);
$('situation').each(function () {
let situation = [];
async.eachOf($(this).find('situationRecord'), function (value, key, callback) {
innerLoop(callback);
}, function (err, situation) {
if (err) {
return console.error(err.message);
}
console.log("from outter", situation.length);
// this will run only if the inner loops completes
if (situation.length > 0) { //if situation is not empty
locations.push(situation);
}
});
});
console.log(locations);
};
function innerLoop(callback) {
let startLocationCode = $(this).find('alertCMethod2SecondaryPointLocation').find('specificLocation').text();
let endLocationCode = $(this).find('alertCMethod2PrimaryPointLocation').find('specificLocation').text();
let overallStartTime = $(this).find('overallStartTime').text();
let overallEndTime = $(this).find('overallEndTime').text();
if (isInvalid(startLocationCode, overallStartTime, overallEndTime)) {
return callback('some error msg');
}
Promise.all([
locationCodeToGeodataRequst.geodataByLocationcode(startLocationCode),
locationCodeToGeodataRequst.geodataByLocationcode(endLocationCode)
]).then(values => {
return createSituationRecord($, this, startLocationCode, endLocationCode, values[0], values[1]);
}).then((obj) => {
return callback(null, obj);
}).catch((err) => {
console.log('err', err.stack);
return callback(err);
});
}
function isInvalid(startLocationCode, startTime, endTime) {
let timestamp = new Date().toDateInputValue();
let isEmptyCode = startLocationCode === '';
let isYetToStart = new Date(timestamp) < new Date(startTime);
let isOver = new Date(timestamp) > new Date(endTime);
return isEmptyCode || isYetToStart || isOver;
}
You should take a deeper look into promises because they are the way to go for synchronous operations. Maybe try to merge your code into functions.