How to nest nightwatch.js commands in page sections? - javascript

I have a page pages/login.js looks like:
function fillAndSubmitLogin(email, password) {
return this
.waitForElementVisible('#emailInput')
.setValue('#emailInput', email)
.setValue('#passwordInput', password)
.waitForElementVisible('#loginSubmitButton')
.click('#loginSubmitButton');
}
export default {
commands: [
fillAndSubmitLogin
],
elements: {
emailInput: 'input#email',
passwordInput: 'input[type=password]',
TFAInput: 'input#token',
loginSubmitButton: '.form-actions button.btn.btn-danger'
}
};
I have another page pages/hompage.js homepage.js attempts to include pages/login.js as a section
import login from "./login.js";
module.exports = {
url: 'http://localhost:2001',
sections: {
login: {
selector: 'div.login-wrapper',
...login
}
}
};
I then have a test case that attempts to login on the hompage section
'Homepage Users can login': (client) => {
const homepage = client.page.homepage();
homepage
.navigate()
.expect.section('#login').to.be.visible;
const login = homepage.section.login;
login
.fillAndSubmitLogin('user#test.com', 'password');
client.end();
}
This test then fails with the following error
TypeError: login.fillAndSubmitLogin is not a function
at Object.Homepage Users can login (/Users/kevzettler//frontend/test/nightwatch/specs/homepage.spec.js:32:6)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:182:7)
login.fillAndSubmitLogin is not a function
at Object.Homepage Users can login (/Users/kevzettler//frontend/test/nightwatch/specs/homepage.spec.js:32:6)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:182:7)

According to the Nightwatch docs, any commands that are exported in page objects should be plain JavaScript objects with a key being a command name and the value being a function. For example:
var googleCommands = {
submit: function() {
this.api.pause(1000);
return this.waitForElementVisible('#submitButton', 1000)
.click('#submitButton')
.waitForElementNotPresent('#submitButton');
}
};
module.exports = {
commands: [googleCommands],
elements: //...etc ...
// etc...
}
In this example, the module exports googleCommands, which is a command object which has a key (submit) and a corresponding function. I believe you should refactor your code as follows:
function fillAndSubmitLogin = {
fillAndSubmitLogin: function(email, password) {
return this
.waitForElementVisible('#emailInput')
.setValue('#emailInput', email)
.setValue('#passwordInput', password)
.waitForElementVisible('#loginSubmitButton')
.click('#loginSubmitButton');
}
};
Of course, you don't have to make the command name the same in both places (as the example shows (googleCommands/submit). This allows you to expose a variety of functions in one command. Hope that answers the question!

Related

Cannot read values from file in fixture folder, getting error as "TypeError Cannot read properties of undefined (reading 'data')"

I'm trying to use fixtures to hold data for different tests, specifically user credentials. This is an example of the code. I'm getting 'Cannot read properties of undefined (reading 'data')'. I tried to google search , I found Cypress fixtures - Cannot read properties of undefined (reading 'data')
I used closure variable technique as reccomended in that post , yet I got reference error of unable to reference data.Please help me.I know cypress.config can be used but I want to keep that for global configs
Json(credentials.json):
{
"username":"*****",
"password":"*****"
}
Code:
import { LoginPage } from "./pageobject/login_page"
describe('Test Scenario', () => {
before(function () {
cy
.fixture('credentials').then(function (data) {
this.data = data
})
})
it('Simple login', () => {
cy.visit(Cypress.env('url'))
var loginpage = new LoginPage()
loginpage.EnterUsername(this.data.username)
loginpage.clickonSubmit()
loginpage.EnterPassword(this.data.password)
loginpage.clickonSubmit()
Cypress
.on('uncaught:exception', (err, runnable) => {
return false;
});
cy.
wait(10000)
cy.
get('span[id="user"]').should('have.text', this.data.username , 'User Login Unsuccessfully')
});
});
There's a few things need adjusting
use function () {} syntax in the it() block
use beforeEach() and alias to load the fixture, because data on this can be cleared (especially after login)
move uncaught:exception catcher to the top of the block
don't cy.wait(), instead add timeout to next command
.should() only has two parameters in this case, so use .and() to test the 2nd text
import { LoginPage } from './pageobject/login_page';
describe('Test Scenario', () => {
beforeEach(function () {
cy.fixture('credentials').as('data')
})
it('Simple login', function() {
Cypress.on('uncaught:exception', (err, runnable) => {
return false;
});
cy.visit(Cypress.env('url'));
var loginpage = new LoginPage();
loginpage.EnterUsername(this.data.username);
loginpage.clickonSubmit();
loginpage.EnterPassword(this.data.password);
loginpage.clickonSubmit();
cy.get('span[id="user"]', {timout:10_000})
.should('have.text', this.data.username)
.and('have.text', 'User Login Unsuccessfully')
})
})
I suspect it's because you are using an arrow function instead of a regular function, you cannot access the this object with an arrow function.
Cypress docs
If you store and access the fixture data using this test context
object, make sure to use function () { ... } callbacks. Otherwise the
test engine will NOT have this pointing at the test context.
change it to this:
it('Simple login', function() {
...
});

SvelteKit Maintenance Mode

Is there a good way to do display a maintenance page when visiting any route of my SvelteKit website?
My app is hosted on Vercel, for those who want to know.
What I've tried so far:
Set an environment variable called MAINTENANCE_MODE with a value 1 in Vercel.
For development purposes I've set this in my .env file to VITE_MAINTENANCE_MODE and called with import.meta.env.VITE_MAINTENANCE_MODE.
Then inside +layout.server.js I have the following code to redirect to /maintenance route
import { redirect } from "#sveltejs/kit";
export async function load({ url }) {
const { pathname } = url;
// Replace import.meta.env.VITE_MAINTENANCE_MODE with process.env.MAINTENANCE_MODE in Production
if (import.meta.env.VITE_MAINTENANCE_MODE == 1) {
if (pathname == "/maintenance") return;
throw redirect(307, "/maintenance");
  } else {
if (pathname == "/maintenance") {
throw redirect(307, "/");
    };
  };
};
What I've also tried is just throwing an error in +layout.server.js with the following:
import { error } from "#sveltejs/kit";
export async function load() {
if (import.meta.env.VITE_MAINTENANCE_MODE == 1) {
throw error(503, "Scheduled for maintenance");
  };
};
However this just uses SvelteKit's static fallback error page and not +error.svelte. I've tried creating src/error.html in the hope to create a custom error page for +layout.svelte but couldn't get it to work.
I would like to use a custom page to display "Down for maintenance", but I don't want to create an endpoint for every route in my app to check if the MAINTENANCE_MODE is set to 1.
Any help is appreciated
You could use a handle server hook, e.g. src/hooks.server.ts:
import { env } from '$env/dynamic/private';
import type { Handle } from '#sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
if (env.MAINTENANCE_MODE == '1' && event.routeId != '/maintenance')
return new Response(undefined, { status: 302, headers: { location: '/maintenance' } });
// <other logic>
// Default response
return await resolve(event);
}
And on the maintenance page you can prevent all further navigation:
import { beforeNavigate } from '$app/navigation';
beforeNavigate(async ({ cancel }) => {
cancel();
});
(Possibly add some periodic checks via fetch calls to navigate elsewhere once the site is back online.)
You can also use +layout.ts to hook up for the maintenance mode. You can even make this conditional for some parts of the site (have frontpage still up and running).
Here is the trick we use:
import type { LayoutLoad } from './$types';
import { chainsUnderMaintenance } from '$lib/config';
import { error } from '#sveltejs/kit';
export const load: LayoutLoad = ({ params }) => {
// Check chain maintenance status; if under maintenance, trigger error (see +error.svelte)
const chainName = chainsUnderMaintenance[<string>params.chain];
if (chainName) {
throw error(503, `Chain under maintenance: ${chainName}`);
}
};

nuxtjs/composition-api - Module is undefined on composable file

I'm using #nuxtjs/composition-api in my Nuxt application with Vuetify. I have a composable file like this:
// composables/dialog.js
// https://www.npmjs.com/package/vuetify-dialog
import { useContext } from '#nuxtjs/composition-api'
export function useDialog () {
const { $dialog } = useContext() // for some reason $dialog is undefined here
const { message, error, info, warning, notify } = $dialog
function showInfo (title, text) {
return info({ title, text })
}
function messageSuccess (text) {
return message.success(text)
}
// ... some other functions with message, error, info, warning
return {
$dialog,
showInfo,
messageSuccess,
// ...
}
}
The module $dialog is correctly registered in nuxt.config.js: $dialog docs
{
modules: [
// Simple usage
'vuetify-dialog/nuxt',
// ... other modules
]
}
When I access first a page that doesn't use this composable and then navigate to another page that uses the composable, it works fine. However, if I directly open the page that uses the composable, it shows the error cannot destructure property 'message' of '$dialog' as it is undefined. It happens in production and local env.
import { useDialog } from '~/composables/dialog'
const { showInfo, messageSuccess } = useDialog()
Is there any limitation with composable files and #nuxtjs/composition-api ?
Github Issue

Creating user on the server with random username and no password

I am very new to Meteor and trying to develop an online synchronous game experiment. Generally, once participants AGREE to the consent form, I want to create a user and add that user into my Players collection.
My consent_page.js looks like this:
import './consent_page.html';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { Template } from 'meteor/templating';
import { Meteor } from 'meteor/meteor'
import '../../../api/players/methods.js'
Template.consent_page.events({
'submit .consent-form'(event) {
event.preventDefault();
Meteor.call('players.addPlayer');
FlowRouter.go('/instructions')
}
});
and my players.addPlayer method looks like this
import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import { Players } from './players.js';
import { Random } from 'meteor/random'
Meteor.methods({
'players.addPlayer'() {
console.log('I am in the method');
username = Random.id();
user = create_user(username);
alert(username);
alert(user);
Players.insert({
_id: this.userId,
enterTime: new Date(),
status: 'instructions',
passedQuiz: false,
quizAttempts: 0,
needRematch: false,
condition: 'control'
});
}
});
/*
* Helper functions for the methods
*/
//create user in the server
create_user = function (username) {
return Accounts.createUser({
username: username,
});
};
The collection Players.js has the definition of the collection.
import { Mongo } from 'meteor/mongo'
export const Players = new Mongo.Collection('players');
However, this doesn't work. I do get redirected to the instructions page, but the user doesn't get created .. I get the following error:
Error invoking Method 'players.addPlayer': Method 'players.addPlayer' not found [404]
Although, I get the I am in the method message printed in the console. The alert with the return of create_user is undefined. Also, I want to create the users without a password (how can I do that?).
Accounts.createUser() method is a part of accounts-base package. You need to import that at first. I'm sharing a snippet from my working project. Hope this helps:
import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
const bcrypt = require('bcrypt');
Meteor.methods({
'user.create':function(data){
return Accounts.createUser({
name: data.name,
username: data.userId,
password: data.password,
});
}
});

How to export this function in Node.js into another module

I am using protractor to test an Angular JS application.
I would like to use page object pattern therefor I need to export a function into another module.
Here is my page-login.js file:
require("./login.spec.js");
var LoginPage = function ()
{
this.userName = element(by.id('login_form_user_input'));
this.password = element(by.id('login_form_password_input')) ;
this.loginButton = element(by.id('login_form_signin_button'));
this.loginText = element(by.css('#mainGlobalSearchBtn'));
this.loginError = element(by.xpath('html/body/div[1]/div[1]/div[1]/form/div/p'));
this.login = function (userName, password)
{
LoginPage.userName.sendKeys(userName);
LoginPage.password.sendKeys(password);
LoginPage.loginButton.click ();
browser.waitForAngular ();
};
};
exports.LoginPage = LoginPage;
Now in another js file called login.spec.js under the same folder I would like to call the login this method :
var util = require ('util');
require("./myconfig.js");
describe('Login', function() {
var loginPage = require("./login-page.js");
var ptor;
beforeEach (function ()
{
ptor = protractor.getInstance();
ptor.get(settings.winURL);
waits(2000);
//Delete cookies to avoid saving password or username
});
it('should not login : incorrect login details', function()
{
loginPage.login('incorrectusername','incorrectpassword');
expect(loginPage.loginError.getText()).toContain('Access denied');
});
When Launching this code protractor is not entering username and passwoed in corresponding fields and I got this console ouput :
1) Login should not login : incorrect login details
Message:
TypeError: Object #<Object> has no method 'login'
Stacktrace:
TypeError: Object #<Object> has no method 'login'
at null.<anonymous> (C:\******\login.spec.js:34:20)
here is line 34 that is throwing the error :
loginPage.login('incorrectusername','incorrectpassword');
How can I use the login functioninside the login page function in another module?
The require( './login-page.js' ) returns a "class" function. It means that, to call login method of loginPage module you might to use new. Ex.:
it('should not login : incorrect login details', function()
{
var page = new loginPage();
page.login('incorrectusername','incorrectpassword');
expect(page.loginError.getText()).toContain('Access denied');
});
Remove the require("./login.spec.js"); of the begin of your module. The test is running before the module creation.
Protractor has this awesome option called onPrepare. You put this in your config.js file. Your onPrepare can reference a file.
In this file you can have all the variables and functions you need!
Instead of "this" you use global.
global.userName = element(by.id('login_form_user_input'));
global.password = element(by.id('login_form_password_input')) ;
global.loginButton = element(by.id('login_form_signin_button'));
global.loginText = element(by.css('#mainGlobalSearchBtn'));
global.loginError = element(by.xpath('html/body/div[1]/div[1]/div[1]/form/div/p'));
global.login = function (user, pw)
{
userName.sendKeys(user);
password.sendKeys(pw);
loginButton.click ();
browser.waitForAngular ();
};
then in your config you do:
//other config settings,
onPrepare: myvariables.js
in your spec, you don't need to require anything!
Just call what you need (minus the global)
beforeEach (function () {
ptor = protractor.getInstance();
ptor.get(settings.winURL);
waits(2000);
//Delete cookies to avoid saving password or username
});
it('should not login : incorrect login details', function()
{
login('incorrectusername','incorrectpassword');
expect(loginError.getText()).toContain('Access denied');
});
you also might want to name your userName variable and the userName arg and password different things. (fixed above)

Categories

Resources