I am currently working on coding a search results page which is set up using a repeater so that the repeater container is able to redirect users to a Dynamic Page.
The datasets used on both the repeater and the dynamic pages are linked by the 'number' field in the relevant collections. So if the repeater is showing the detail for "1" in the number field then the user can click on the container box and be redirected to the dynamic page for "1". I have managed to get this working fine in that I am redirected to the correct Dynamic Page when I click on the relevant container. However, some numbers which exist in the dataset for the repeater don't exist in the dataset for the Dynamic Pages. So I could have numbers 1-50 in the dataset for the repeater but only 20 of those exist in the dataset for the dynamic pages and at the moment if the container contains a number that doesn't exist in the dynamic page, it still tries to redirect but just leads to a 404 error page where I would prefer it that, in this situation, the container is simply not clickable.
I have so far tried the following code:
$w('#Dataset1').onReady(() => {
wixData.query('Collection1')
.eq('number', number1)
.find()
.then(res => {
const number1 = res.number1;
})
.catch ((err) => {
let errorMsg = err;
});
wixData.query('Collection2')
.eq('number', number2)
.find()
.then(res => {
const number2 = res.number2;
})
.catch ( (err) => {
let errorMsg = err;
});
if (number1 === number2){
$w("#container2").onClick( (event) => {
let $item = $w.at(event.context);
let currentItem = $item('#Dataset1').getCurrentItem();
let dynamicPageID = `${currentItem.number}`
let collectionID = `${currentItem.title}`
console.log('https://www.mywebsite.com/'+collectionID+'/'+dynamicPageID);
wixLocation.to('https://www.mywebsite.comk/'+collectionID+'/'+dynamicPageID);
});
}
});
}
But this still redirects to dynamic pages where the number doesn't exist and sends to a 404 error!
Could anyone kindly point me in the right direction please?
Thanks
After Sam's suggestion regarding async/await, I have been playing around with the code but still failing to find a working solution. I have tried the below:
async function linktodynamicpage () {
$w('#Dataset1').onReady(() => {
wixData.query('Collection1')
.eq('number', number1)
.find()
.then(async(res) => {
const number1 = await res.number1;
})
.catch ((err) => {
let errorMsg = err;
});
wixData.query('Collection2')
.eq('number', number2)
.find()
.then(async(res) => {
const number2 = await res.number2;
})
.catch ( (err) => {
let errorMsg = err;
});
if (number1 === number2){
$w("#container2").onClick( (event) => {
let $item = $w.at(event.context);
let currentItem = $item('#Dataset1').getCurrentItem();
let dynamicPageID = `${currentItem.number}`
let collectionID = `${currentItem.title}`
console.log('https://www.mywebsite.com/'+collectionID+'/'+dynamicPageID);
wixLocation.to('https://www.mywebsite.comk/'+collectionID+'/'+dynamicPageID);
});
}
});
}
I have tried a few variations of this, trying to move the async about but to no avail unfortunately.
Just thought I would see if I am getting the async code right or if there are errors. That way, at least I know if the async/await option is a viable solution to my problem.
Thanks
My guess is that this has something to do with the Promises in your code. At the time you check if number1 is equal to number2 your queries have not resolved and they are therefore both undefined and always equal to each other.
To solve the problem, you can switch to using async/await syntax, or use Promise.all() to wait for both queries to resolve before doing the comparison.
The edit still doesn't wait for the queries to resolve. You need something more like this:
async function linktodynamicpage () {
$w('#Dataset1').onReady(async () => {
const number1 = await wixData.query('Collection1')
.eq('number', number1)
.find();
const number2 = await wixData.query('Collection2')
.eq('number', number2)
.find();
if (number1 === number2){
// the rest of the code...
Related
1: The mat-select has 4 values, 1,2,3,4.
The code below works good for the select. So I'd like to share if it helps the readers.
it('check the length of drop down', async () => {
const trigger = fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement;
trigger.click();
fixture.detectChanges();
await fixture.whenStable().then(() => {
const inquiryOptions = fixture.debugElement.queryAll(By.css('.mat-option-text'));
expect(inquiryOptions.length).toEqual(4);
});
});
2: I need another test to verify the default value in the same
mat-select is 3 or not. When page loads the default value for the drop down is set to 3.
it('should validate the drop down value if it is set by default', async () => {
const trigger = fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement;
trigger.click();
fixture.detectChanges();
await fixture.whenStable().then(() => {
const inquiryOptions = fixture.debugElement.queryAll(By.css('.mat-option-text'));
const value = trigger.options[0].value;
expect(value).toContain(3);
});
});
Any help is appreciated.
This one is worked for me in Angular 7
const debugElement = fixture.debugElement;
// open options dialog
const matSelect = debugElement.query(By.css('.mat-select-trigger')).nativeElement;
matSelect.click();
fixture.detectChanges();
// select the first option (use queryAll if you want to chose an option)
const matOption = debugElement.query(By.css('.mat-option')).nativeElement;
matOption.click();
fixture.detectChanges();
fixture.whenStable().then( () => {
const inputElement: HTMLElement = debugElement.query(By.css('.ask-input')).nativeElement;
expect(inputElement.innerHTML.length).toBeGreaterThan(0);
});
After some testing I found an answer (at least for my code) and hope, that this is helpful to you as well:
When I looked at the DOM, when the application is running, I noticed that the default value of the mat-select is inside this DOM structure:
<mat-select>
<div class="mat-select-trigger">
<div class="mat-select-value">
<span class="something">
<span class="something">
The value is here!
But in my case, I had a form builder in my .ts file and it was used in ngOnInit(). It seems that the normal TestBed.createComponent(MyComponent) does not call ngOnInit(). So I had to do that in order to get the value. Otherwise there was just a placeholder span.
So, my final code looks like this:
it('should validate the drop down value if it is set by default', async () => {
const matSelectValueObject: HTMLElement = fixture.debugElement.query(By.css('.mat-select-value')).nativeElement;
component.ngOnInit();
fixture.detectChanges();
const innerSpan =
matSelectValueObject.children[0].children[0]; // for getting the inner span
expect(innerSpan.innerHTML).toEqual(3); // or '3', I did not test that
});
By the way I'm using Angular 7, in case this matters.
Helper method for your page object to set the option by text:
public setMatSelectValue(element: HTMLElement, value: string): Promise<void> {
// click on <mat-select>
element.click();
this.fixture.detectChanges();
// options will be rendered inside OverlayContainer
const overlay = TestBed.get(OverlayContainer).getContainerElement();
// find an option by text value and click it
const matOption = Array.from(overlay.querySelectorAll<HTMLElement>('.mat-option span.mat-option-text'))
.find(opt => opt.textContent.includes(value));
matOption.click();
this.fixture.detectChanges();
return this.fixture.whenStable();
}
let loader = TestbedHarnessEnvironment.loader(fixture);
const matSelect = await loader.getAllHarnesses(MatSelectHarness);
await matSelect[0].clickOptions();
const options = await matSelect[0].getOptions();
expect(await options[0].getText()).toMatch("");
expect(await options[1].getText()).toMatch('option1');
expect(await options[2].getText()).toMatch('option2');
const select = await loader.getAllHarnesses(MatSelectHarness);
//test to check there are how many mat selects
expect(select.length).toBe(1);
//open the mat select
await select[0].open();
//Get the options
const options = await select[0].getOptions();
//test to check option length
expect(options.length).toBe(1);
//test to check mat options
expect(await options[0].getText()).toBe('option 1');
expect(await options[1].getText()).toBe('option 2');
I'm need to 'Nodejs' and 'Serveless'. I've created a 'Serverless' API and deployed to AWS. Everything works as expected. The issue i have and i can't seem to find anything about this is, on every second call i get an internal server error. the first call is, returns data as expected.
I've deployed to AWS only in a dev stage. I'm wondering if there is some configuration i'm missing or something?
If you need the 'Serverless' config or code examples i can provide.
Thanks.
ANSWER
I think there was an issue with the DB call not returning data in time for the callback, therefore i was finding inconsistent results.
So basically what i did was create a Database class returning Promises like so...
'use strict';
const mysql = require('mysql');
/**
* Database
*/
class Database {
constructor(config) {
if (!this.dbConnection) {
console.log('connect to DB');
this.dbConnection = mysql.createPool(config);
this.dbConnection.on('connection', (connection) => {
console.info('Connection Made!');
});
}
}
query(sql, args) {
return new Promise((resolve, reject) => {
this.dbConnection.query(sql, args, (err, rows) => {
if (err) {
reject(err);
}
resolve(rows);
})
});
}
close() {
return new Promise((resolve, reject) => {
this.dbConnection.end((error) => {
if (error) {
reject(error);
}
resolve();
});
});
}
}
module.exports = Database;
So when i made my query there was a result ready for the callback.
'use strict';
const Database = require('./lib/Database');
const {successResponse, errorResponse} = require('./lib/response');
const CategoryResource = require('./resource/Category');
module.exports.list = (event, context, callback) => {
let sql = 'SELECT * FROM categories AS c WHERE c.company_id = ? AND c.parent_id IS NULL AND c.status = 1 LIMIT ?, ?;';
const company = parseInt(event.queryStringParameters.company);
let page = 1;
let limit = 20;
if (null != event.queryStringParameters) {
if ('page' in event.queryStringParameters) {
page = parseInt(event.queryStringParameters.page);
}
if ('limit' in event.queryStringParameters) {
limit = parseInt(event.queryStringParameters.limit);
}
}
let start = (page - 1) * limit;
if (isNaN(company)) {
callback(null, errorResponse(400, 'Company ID Required', 'Parameter company_id is required.', []));
return;
}
let Category = new Database();
let categoryResource = [];
Category
.query(sql, [company, start, limit])
.then(response => {
Category.close();
response.forEach((category) => {
categoryResource.push(CategoryResource(category));
});
callback(null, successResponse(200, {
"total": response.length,
"perPage": limit,
"currentPage": page,
"data": categoryResource
}));
})
.catch((error) => {
callback(null, errorResponse(error.code, error.sqlMessage, error.sql, {
code: error.errno,
field: error.sqlMessage,
message: error.sqlMessage
}));
Category.close();
});
};
I hope that helps anyone that may have run into the same issue.
If every other time you get an internal server error, that means your code is syntactically sound but has some sort of logic error. It's impossible to help without example code, but some of the more common errors I've seen that only sometimes occur can be:
race conditions (if you're doing parallel access of the same array, for example)
array access errors (length+1 instead of length-1, less-than-zero, or your iterators are jumping someplace in memory they shouldn't)
simply mentioning the wrong variable (putting an i instead of a j, for example)
Unfortunately, without specific examples, the best we can offer is wild speculation and personal experience. Have you tried looking at AWS's CloudWatch and what it says about your execution? There should be some errors logged in there too.
I think there was an issue with the DB call not returning data in time for the callback, therefore i was finding inconsistent results.
So basically what i did was create a Database class returning Promises like so...
'use strict';
const mysql = require('mysql');
/**
* Database
*/
class Database {
constructor(config) {
if (!this.dbConnection) {
console.log('connect to DB');
this.dbConnection = mysql.createPool(config);
this.dbConnection.on('connection', (connection) => {
console.info('Connection Made!');
});
}
}
query(sql, args) {
return new Promise((resolve, reject) => {
this.dbConnection.query(sql, args, (err, rows) => {
if (err) {
reject(err);
}
resolve(rows);
})
});
}
close() {
return new Promise((resolve, reject) => {
this.dbConnection.end((error) => {
if (error) {
reject(error);
}
resolve();
});
});
}
}
module.exports = Database;
So when i made my query there was a result ready for the callback.
'use strict';
const Database = require('./lib/Database');
const {successResponse, errorResponse} = require('./lib/response');
const CategoryResource = require('./resource/Category');
module.exports.list = (event, context, callback) => {
let sql = 'SELECT * FROM categories AS c WHERE c.company_id = ? AND c.parent_id IS NULL AND c.status = 1 LIMIT ?, ?;';
const company = parseInt(event.queryStringParameters.company);
let page = 1;
let limit = 20;
if (null != event.queryStringParameters) {
if ('page' in event.queryStringParameters) {
page = parseInt(event.queryStringParameters.page);
}
if ('limit' in event.queryStringParameters) {
limit = parseInt(event.queryStringParameters.limit);
}
}
let start = (page - 1) * limit;
if (isNaN(company)) {
callback(null, errorResponse(400, 'Company ID Required', 'Parameter company_id is required.', []));
return;
}
let Category = new Database();
let categoryResource = [];
Category
.query(sql, [company, start, limit])
.then(response => {
Category.close();
response.forEach((category) => {
categoryResource.push(CategoryResource(category));
});
callback(null, successResponse(200, {
"total": response.length,
"perPage": limit,
"currentPage": page,
"data": categoryResource
}));
})
.catch((error) => {
callback(null, errorResponse(error.code, error.sqlMessage, error.sql, {
code: error.errno,
field: error.sqlMessage,
message: error.sqlMessage
}));
Category.close();
});
};
I hope that helps anyone that may have run into the same issue.
Here is a test stub that I have written in Mocha/Chai. I can easily dispatch an action and assert that the state is equal to what I expect, but how do I validate that it followed the expected process along the way (IE the earlier tests)?
/**
* This test describes the INITIALIZE_STATE action.
* The action is asynchronous using the async/await pattern to query
* The database. The action creator returns a thunk which should in turn
* return the new state with a list of tables and their relationships with eachother
**/
describe('Initialize state', () => {
it('Should check if state is empty', () => {});
it('Should check if tables/relationships exist', () => {});
it('Should check if new tables have been added', () => {});
it('Should merge new and existing tables and relationships', () => {
// Here is where we would dispatch the INITIALIZE_STATE
// action and assert that the new state is what I expect it to be.
});
});
I haven't written any code for the actual action itself yet, because I want the code to pass these validations. Some psuedo-code might look like this
export function initializeState() {
return function(dispatch) {
let empty = store.getState().empty
let state = (empty) ? await getLastPersistedState() : store.getState()
let tables = state.tables;
let payload = tables.concat(await getNewTables(tables));
dispatch({type: 'INITIALIZE_STATE', payload});
}
}
function getLastPerisistedState() {
return mongodb.findall(state, (s) => s);
}
function getNewTables(tableFilter) {
return sql.query("select table_name from tables where table_name not in (" + tableFilter + ")");
}
Here is the solution that I came up with. There may be a better one, but so far no one has been able to provide one. I decided to go with a refactored set of actions and a separate store for my testing. The actions are function generators rather than using thunk. They yield out the actions that thunk will dispatch in the production code. In my test I can then dispatch those actions myself and verify that the resulting state is what I'd expect. This is exactly what thunk will do but it allows me to insert myself as the middle man rather than depending on the thunk middle-ware.
This is also super useful because it makes separating the action logic from the dispatch and state logic extremely easy, even when you are testing asynchronous flow.
For the database, I automatically generate a stub and use promises to simulate the async query. Since this project is using sequelize anyways, I just used sequelize to generate the stub.
Here is the code
_actions.js
export function *initializeState() {
//We will yield the current step so that we can test it step by step
//Should check if state is empty
yield {type: 'UPDATE_STEP', payload: 'IS_STATE_EMPTY'};
yield {type: 'UPDATE_STEP_RESULT', payload: stateIsEmpty()};
if(stateIsEmpty()) {
//todo: Implement branch logic if state is empty
}
//...
}
sequelize/_test/generate.js
async function createMockFromSql(db, sql, filename) {
let results = await db.query(sql, {type: db.Sequelize.QueryTypes.SELECT});
return new Promise((resolve, reject) => {
// Trim the results to a reasonable size. Keep it unique each time
// for more rigorous testing
console.log('trimming result set');
while (results.length > 50) {
results.splice(results.length * Math.random() | 0, 1);
}
fs.writeFile(path.resolve(__dirname, '../../sequelize/_test', filename), JSON.stringify(results, null, 2), err => {
if (err) {
console.error(err);
reject(false);
}
resolve(true);
})
})
}
test/actions.js
...
it('Should check if state is empty', () => {
let action = initializeState();
expect(action.next()).to.deep.equal({type: 'UPDATE_STEP', payload: 'IS_STATE_EMPTY'})
});
I am facing a problem in writing a test case using Mocha and Chai. In the following code value of n is 1. I checked it using console.log(). Though I am testing this value against 0 the test is still passing. Actually it doesn't matter against what value n is tested, the test is passing anyway. What is the problem in it? Can anyone help?
it("Should have 1 variables", function(){
var variable_count = require("../../lib/variable_count").variable_count;
var file = __dirname + '/cases/case_3.json';
jsonfile.readFile(file, function(err, obj) {
var n = variable_count(obj);
expect(n).to.equal(0);
assert.strictEqual(n, 0);
});
});
The issue is that your code is asynchronous (because of jsonfile.readFile()), but your test is synchronous.
To make the test asynchronous, so it waits for a result, you can use the following:
it("Should have 1 variables", function(done) {
var variable_count = require("../../lib/variable_count").variable_count;
var file = __dirname + '/cases/case_3.json';
jsonfile.readFile(file, function(err, obj) {
var n = variable_count(obj);
expect(n).to.equal(0);
assert.strictEqual(n, 0);
done();
});
});
More info here.
I'm trying to make 3 API calls and then return an array of all the data. However, the console.log() (and therefore the function return) is empty as it's not waiting for the AJAX call to be resolved - but I can't do it in the loop as I need all the data.
let data = [];
parameters.forEach((parameter, index) => {
return Ember.$.ajax(url).then((response) => {
data.push({
name: parameter.get('displayName'),
color: parameter.get('color'),
type: chart.get('chartType'),
turboThreshold: 0,
data: response.data
});
});
});
console.log(data);
return data;
I think that I can use Ember.RSVP.hash() for this but I can't seem to get it to work... can anyone point me in the right direction?
Return a promise and resolve it when all of the inner promises are resolved. I didn't try out the code, But this should point you how to proceed.
return new Ember.RSVP.Promise(function (resolve) { //return a promise
let promises = [];
let data = [];
parameters.forEach((parameter, index) => {
promises[index] = Ember.$.ajax(url).then((response) => { //store all inner promises
data.push({
name: parameter.get('displayName'),
color: parameter.get('color'),
type: chart.get('chartType'),
turboThreshold: 0,
data: response.data
});
});
});
Ember.RSVP.all(promises).then(function(){
resolve(data); //resolve the promise when all inner promises are resolved
});
});
Actually you can't wait for promises unless you are in the routes' model hooks.
See model hooks on the guide
You have another option, try to set data directly to the item when data is resolved. (inside the then function)
You could try something like this :
return Promise.all(parameters.map(parameter => Ember.$.ajax(url).then(response => {
'name': parameter.get('displayName'),
'color': parameter.get('color'),
'type': chart.get('chartType'),
'turboThreshold': 0,
'data': response.data
}))).then(data => {
console.log(data);
return data;
});