Nightwatch(PageObject) access elements from different page object - javascript

Is there a way to access elements defined in one page object file from another page object file?
Example:
If we need to access '#usernameInput' from LoginPage.ts file, do we need to duplicate it from HomePage.ts ? Is there any other way?
HomePage.ts
const page: PageObject = {
url: ...,
commands: [
enterUsername: (query: string) => {
return this
.waitForElementVisible('#usernameInput')
}],
elements: {
usernameInput: {
locateStrategy: 'xpath',
selector: '//input[#name="Username"]',
}
};
LoginPage.js
const page: PageObject = {
url: ...,
commands: [
enterUsername: (query: string) => {
return this
.waitForElementVisible('#usernameInput')
}],
elements: {}
};

You can access one page object from another by using this.api.page.pageObjectName.
In your example you would just do
const loginPage = this.api.page.loginPage();
And then to get the usernameInput element you can just do this
const usernameInput = loginPage.elements.usernameInput.selector;
So your enterUsername function should look something like the following
enterUsername: (query: string) => {
const loginPage = this.api.page.loginPage();
const usernameInput = loginPage.elements.usernameInput.selector;
return this
.waitForElementVisible(usernameInput)
.setValue(usernameInput, query);
}

Use LocalStoreage to keep your data and access in all the page using Javascript or Jquery
Set your Object
localStorage.setItem("PortPlans", PortPlans);
call your localstorage in another page
var PortPlans = localStorage.getItem("PortPlans");
console.log(PortPlans);

Related

Sharing variables between functions in javascript

if this question is not clear, let me know and I will exapand on it. Not great with JS.
I have a js file. let's call it jsFile1.js in which I have two methods. Method1 is being called from another file (anotherJsFile.js) and that call sends a variable to the Method1 in jsFile1.js.
Now I want my second method, Method2, which is being called from inside jsFile1.js to also be able to use the variable sent from anotherJsFile to method 1.
Have tried using id's and set value etc but it won't work. Any suggestions? Presume I have to store const tmp in the config or init and then access it from the Method2?
File1
Method1(item, table) {
//item is a marked item from the table, table contains all entries
const tmp = {table, id: "tmpTable"};
}
Method2() {
const data = this.$$("tmpTable").getValues();
}
config() {
const Method2Button = {
view:"button",
label:"Method2",
click: () => this.Method2()
}}
Just write a method that takes an integer and import the class in you constructor
File1.js
private test: any
Method1(item, table) {
const tmp = {table, id: "tmpTable"};
this.test = tmp
}
Method2() {
return this.test.id //.getValues() ??
// return this.class.getValues(this.test.id)
}
config() {
const Method2Button = {
view:"button",
label:"Method2",
click: () => this.Method2()
}}

How to store nuxtjs dynamically generated routes in vuex store

I'm trying to leverage nuxtjs SSG capabilities by creating a static web site where the pages content and navigation are fetched from an API.
I already found my way around on how to dynamically generate the routes by defining a module where I use the generate:before hook to fetch the pages content and routes. When creating the routes I store the page content as the route payload. The following code does just that and works as intended.
modules/dynamicRoutesGenerator.js
const generator = function () {
//Before hook to generate our custom routes
this.nuxt.hook('generate:before', async (generator, generatorOptions) => {
generator.generateRoutes(await generateDynamicRoutes())
})
}
let generateDynamicRoutes = async function() {
//...
return routes
}
export default generator
Now the problem I'm facing is that I have some navigation components that need the generated routes and I was thinking to store them into the vuex store.
I tried the generate:done hook but I don't know how to get the vuex store context from there. What I ended up using was the nuxtServerInit() action because as stated in the docs:
If nuxt generate is ran, nuxtServerInit will be executed for every dynamic route generated.
This is exactly what I need so I'm trying to use it with the following code:
store/index.js
export const actions = {
nuxtServerInit (context, nuxtContext) {
context.commit("dynamicRoutes/addRoute", nuxtContext)
}
}
store/dynamicRoutes.js
export const state = () => ({
navMenuNivel0: {}
})
export const mutations = {
addRoute (state, { ssrContext }) {
//Ignore static generated routes
if (!ssrContext.payload || !ssrContext.payload.entrada) return
//If we match this condition then it's a nivel0 route
if (!ssrContext.payload.navMenuNivel0) {
console.log(JSON.stringify(state.navMenuNivel0, null, 2));
//Store nivel0 route, we could use url only but only _id is guaranteed to be unique
state.navMenuNivel0[ssrContext.payload._id] = {
url: ssrContext.url,
entrada: ssrContext.payload.entrada,
navMenuNivel1: []
}
console.log(JSON.stringify(state.navMenuNivel0, null, 2));
//Nivel1 route
} else {
//...
}
}
}
export const getters = {
navMenuNivel0: state => state.navMenuNivel0
}
The action is indeed called and I get all the expected values, however it seems like that with each call of nuxtServerInit() the store state gets reset. I printed the values in the console (because I'm not sure even if it's possible to debug this) and this is what they look like:
{}
{
"5fc2f4f15a691a0fe8d6d7e5": {
"url": "/A",
"entrada": "A",
"navMenuNivel1": []
}
}
{}
{
"5fc2f5115a691a0fe8d6d7e6": {
"url": "/B",
"entrada": "B",
"navMenuNivel1": []
}
}
I have searched all that I could on this subject and altough I didn't find an example similar to mine, I put all the pieces I could together and this was what I came up with.
My idea was to make only one request to the API (during build time), store everything in vuex then use that data in the components and pages.
Either there is a way of doing it better or I don't fully grasp the nuxtServerInit() action. I'm stuck and don't know how to solve this problem and can't see another solution.
If you made it this far thanks for your time!
I came up a with solution but I don't find it very elegant.
The idea is to store the the API requests data in a static file. Then create a plugin to have a $staticAPI object that expose the API data and some functions.
I used the build:before hook because it runs before generate:before and builder:extendPlugins which means that by the time the route generation or plugin creation happen, we already have the API data stored.
dynamicRoutesGenerator.js
const generator = function () {
//Add hook before build to create our static API files
this.nuxt.hook('build:before', async (plugins) => {
//Fetch the routes and pages from API
let navMenuRoutes = await APIService.fetchQuery(QueryService.navMenuRoutesQuery())
let pages = await APIService.fetchQuery(QueryService.paginasQuery())
//Cache the queries results into staticAPI file
APIService.saveStaticAPIData("navMenuRoutes", navMenuRoutes)
APIService.saveStaticAPIData("pages", pages)
})
//Before hook to generate our custom routes
this.nuxt.hook('generate:before', async (generator, generatorOptions) => {
console.log('generate:before')
generator.generateRoutes(await generateDynamicRoutes())
})
}
//Here I can't find a way to access via $staticAPI
let generateDynamicRoutes = async function() {
let navMenuRoutes = APIService.getStaticAPIData("navMenuRoutes")
//...
}
The plugin staticAPI.js:
import APIService from '../services/APIService'
let fetchPage = function(fetchUrl) {
return this.pages.find(p => { return p.url === fetchUrl})
}
export default async (context, inject) => {
//Get routes and files from the files
let navMenuRoutes = APIService.getStaticAPIData("navMenuRoutes")
let pages = APIService.getStaticAPIData("pages")
//Put the objects and functions in the $staticAPI property
inject ('staticAPI', { navMenuRoutes, pages, fetchPage })
}
The APIService helper to save/load data to the file:
//...
let fs = require('fs');
let saveStaticAPIData = function (fileName = 'test', fileContent = '{}') {
fs.writeFileSync("./static-api-data/" + fileName + ".json", JSON.stringify(fileContent, null, 2));
}
let getStaticAPIData = function (fileName = '{}') {
let staticData = {};
try {
staticData = require("../static-api-data/" + fileName + ".json");
} catch (ex) {}
return staticData;
}
module.exports = { fetchQuery, apiUrl, saveStaticAPIData, getStaticAPIData }
nuxt.config.js
build: {
//Enable 'fs' module
extend (config, { isDev, isClient }) {
config.node = { fs: 'empty' }
}
},
plugins: [
{ src: '~/plugins/staticAPI.js', mode: 'server' }
],
buildModules: [
'#nuxtjs/style-resources',
'#/modules/staticAPIGenerator',
'#/modules/dynamicRoutesGenerator'
]

Default string value after call the object in JavaScript

I have a js object in which I return my endpoint addresses from api. This is a very nice solution for me, it looks like this:
export const API_BASE_URL = 'http://localhost:3000';
export const USERS = '/Users';
export default {
users: {
checkEmail: (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`,
notifications: `${API_BASE_URL}${USERS}/notifications`,
messages: `${API_BASE_URL}${USERS}/messages`,
},
};
Now I can call this address in my redux-saga to execute the xhr query:
import { api } from 'utils';
const requestURL = api.users.notifications;
But I'm a bit stuck because now I have a problem - base path is missing here: '/users'.
Now when I call api.users, then I get a object. I would like to have a default value after calling the object like:
import { api } from 'utils';
const requestURL = api.users; // http://localhost:3000/Users
const requestURL2 = api.users.notifications; // http://localhost:3000/Users/notifications
I know that I could add a new string with the name 'base' to the object and add '/Users' there, but I don't like this solution and I think, there is a better solution.
You could do one of the following:
extend the String class
const API_BASE_URL = "http://localhost:3000"
const USERS = "/Users"
class UsersEndpoints extends String {
constructor(base) {
super(base)
}
// this is still a proposal at stage 3 to declare instance variables like this
// if u want a truly es6 way you can move them to the constructor
checkEmail = (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`
notifications = `${API_BASE_URL}${USERS}/notifications`
messages = `${API_BASE_URL}${USERS}/messages`
}
// you can use userEndpoints itself as a string everywhere a string is expected
const userEndpoints = new UsersEndpoints(API_BASE_URL)
export default {
users: userEndpoints
}
The previous is just actually equivalent to
...
const userEndpoints = new String(API_BASE_URL)
userEndpoints.notifications = `${API_BASE_URL}${USERS}/notifications`
...
Obviously this is not recommended: you should not extend native classes, there are many disadvantages to this approach.
An obvious example is that there could be a conflict between the properties you use and the properties that might be brought by the native class
override the toString method
...
export default {
users: {
checkEmail: (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`,
notifications: `${API_BASE_URL}${USERS}/notifications`,
messages: `${API_BASE_URL}${USERS}/messages`,
toString: () => API_BASE_URL
},
};
// this is actually not much different than the previous method, since a String is an objet with an overridden toString method.
// That said this method is also not recommended since toString is used in many places in native code, and overriding it just to substitute a string value will make information get lost in such places, error stacks for example
Achieve what u want using the language features intended for such a use case
What you are asking is to make the same variable to have different values in the same time, which is not possible in the language syntax, and it makes sense because it makes it hard to reason about code.
that being said i recommend something of the following nature
// it is also better to use named exports
export const getUsersEndpoint = ({
path = "",
dynamicEndpointPayload = {},
} = {}) => {
switch (path) {
case "notifications":
return `${API_BASE_URL}${USERS}/notifications`
case "messages":
return `${API_BASE_URL}${USERS}/messages`
case "checkEmail":
return `${API_BASE_URL}${USERS}/${dynamicEndpointPayload.email}/checkEmail`
// you still can do checkEmail like this, but the previous is more consistent
// case "checkEmail":
// return (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`
default:
return `${API_BASE_URL}`
}
}
// you can use it like this
getUsersEndpoint() // returns the base
getUsersEndpoint({path: 'notifications'})
You can extend prototype to achieve this behaviour:
export const API_BASE_URL = 'http://localhost:3000';
export const USERS = '/Users';
const users = `${API_BASE_URL}${USERS}`
const baseUrls = {
checkEmail: (email) => `${users}/${email}/checkEmail`,
notifications: `${users}/notifications`,
messages: `${users}/messages`,
}
Object.setPrototypeOf(users.__proto__, baseUrls);
export default {
users
};
Try having object will all user endpoint and a function that return a value of a end point
const user = {
default: '/users',
notification: '/notification',
profile: '/profile',
getEndPoint(prop) {
if(this[prop] === 'default' ){
return this[prop];
} else {
if(this[prop]) {
return this.default + this[prop];
}
}
}
}
So you can have more end points that come under user and you can simply call
const requestURL = api.user.getEndPoint('default'); // http://localhost:3000/Users
const requestURL2 = api.user.getEndPoint('notifications'); // http://localhost:3000/Users/notification

Using Variables with react-apollo Query

I've attached a query to my React Native component like so:
let getNearbyStoriesQuery = gql`{
getStoriesNearbyByGeoHash(geoHash: "8k"){
id
}
}`;
export default graphql(getNearbyStoriesQuery,
{
props: (props) => {
debugger;
let storiesNearby = props.data.getStoriesNearbyByGeoHash.map((story) => {
return {
id: story.id,
}
});
return {storiesNearby};
},
variables: {
geoHash: mockGeoHash
}
})(Home); // Home is the React Native Component
I'm able to retrieve data from using this technique as long as I hardcode a value in for geoHash in the query; in this case I used "8k". However, when I attempt to modify the query so that I can puss in a variable like so:
let getNearbyStoriesQuery = gql`{
getStoriesNearbyByGeoHash($geoHash: String!){
id
}
}`;
I get an error saying Expected Name, found $. This method of passing variables to queries is repeated across multiple sources. What am I doing wrong here?
Should be:
query GetStoriesNearbyByGeoHash($geoHash: String!) {
getStoriesNearbyByGeoHash(geoHash: $geoHash){
id
}
}
Have a look on how to use variables in graphql: http://graphql.org/learn/queries/#variables

Vue 2.0, where to place local functions

Where do you correctly place local functions in vue 2.x?
I could just place them in the "methods" object, but I'd like them to be completely local to the instance if thats possible.
Sort of like this in Plain JS :
window._global = (function () {
function _secretInsideFunct(){
return "FooBar";
}
var __localObject = {
outsideFunct : function () {
return _secretInsideFunct();
}
}
return __localObject;
}());
..where _global._secretInsideFunct() wouldnt be accessible anywhere else but from inside the _global object.
In this specific case I want to make a function that creates an array object if it doesn't exist.. Something like:
function CreateOrSet (workArray, itemName, itemValue ){
var salaryRow = self.Status.Rows.find(r => r.recordID == itemName);
if (!salaryRow) {
salaryRow = { recordID: itemName, recordAmount: 0, recordName: "Løn" };
self.Status.Rows.push(salaryRow);
}
salaryRow.recordAmount = itemValue ;
}
..but a general approach for these cases is better :)
Now this function doesn't looks like a utility or helper function, but relate to a state, Status.Rows. If I were you, I will define it as close as to the state or the module that the state being used.
If the state will be used across the app, maybe I will define it in entry file, index.js or app.vue.
Or If you are using vuex, you can define it as an vuex action. So you may do something like this:
const store = new Vuex.Store({
state: {
status: {
rows: []
},
mutations: {
pushRow (state, salaryRow) {
state.status.rows.push(salaryRow)
},
changeAmount (state, id, amount) {
const salaryRow = state.status.Rows.find(r => r.recordID === id)
salaryRow.recordAmount = amount
}
},
actions: {
createRow (context, itemName) {
const salaryRow = { recordID: itemName, recordAmount: 0, recordName: "Løn" };
context.commit('pushRow', salaryRow)
}
}
})
You can put all of the code to a single action, it is just an idea, how you organize your code depend on your needs.

Categories

Resources