Upgrading from 0.9.19 to 1.1.0 seems to break Page objects - nightwatch.js

I have a page object with this code:
var MyToolComp = require('./MyToolComponent').MyToolComponent;
var MyToolCommand = {
get: function (cssLocator, timeout) {
return new MyToolComp({client: this, locator: cssLocator, timeout: timeout});
},
assertMyToolCount: function (expectedMyToolesCount, timeoutMs) {
console.log('Validate number of MyTool in the page to be [' + expectedMyToolesCount + ']');
this.waitForElementsCount(this.section.john_container.selector, expectedMyToolesCount, timeoutMs);
return this;
},
};
module.exports = {
commands: [MyToolCommand],
sections: {
john_container: {
selector: '.john_container',
elements: {
john_MyTool: {
selector: '.john_MyTool'
},
header: {
selector: '.john_MyTool_header'
}
}
},
multi_widget: {
selector: '.john_multi_widget'
}
}
};
After upgrading to 1.1.0, I can't run this code in my test:
var myToolPage = browser.page.myTool();
myToolPage.assertMyToolCount(1);
When debugging, I see myToolPage has "section" var, but no commands.
If I remove sections and only do
module.exports = MyToolCommand;
or
module.exports = {
commands: [MyToolCommand]
}
Then I can run
myToolPage.assertMyToolCount(1);
But then it failes since
this.section.john_container.selector
Isn't defined.
What am I doing wrong? I can't find anything here or here. Is there anything else I can read that'll help me? What else should I know when upgrading NW? (This is my first time seeing anything nightwatch-related so I kinda have to learn as I go).
Thanks in advance :)

The way I fixed this was replacing
module.exports = {
commands: [MyToolCommand],
sections: {
john_container: {
selector: '.john_container',
elements: {
john_MyTool: {
selector: '.john_MyTool'
},
header: {
selector: '.john_MyTool_header'
}
}
},
multi_widget: {
selector: '.john_multi_widget'
}
}
};
With
exports.commands = MyToolCommand;
exports.sections = {
john_container: {
selector: '.john_container',
elements: {
john_MyTool: {
selector: '.john_MyTool'
},
header: {
selector: '.john_MyTool_header'
}
}
},
multi_widget: {
selector: '.john_multi_widget'
}
};

Related

How do I render embedded code blocks from Contentful with Prism in Astro?

I decided to try out Astro by building a web development blog integrated with contentful. I got everything working except for the code blocks. I create an embedded entry for the code block in my content model, which returns the following nodeType:
{
nodeType: 'embedded-entry-block',
data: {
target: {
metadata: [Object],
sys: [Object],
fields: {
code:
'```html\n<!DOCTYPE html>\n<html>\n<head>\n<title>My First Astro App</title>\n</head>\n <body>\n<h1>Hello, World!</h1>\n</body>\n</html>\n```'
}
}
},
content: []
}
I cannot figure out how to use documentToHtmlString from "#contentful/rich-text-html-renderer" to display the code block in my blog with a themed Prism component.
I passed a custom rendering component for the embedded entry to the documentToHtmlStringoptions, and extracted the lang and HTML code, but then I could not figure out how to return a themed Prism code block. However, I did manage to get it working without styling using the runHighlighterWithAstro function from the #astrojs/prism library.
---
import { documentToHtmlString } from "#contentful/rich-text-html-renderer";
import { BLOCKS } from "#contentful/rich-text-types";
import ContentLayout from "#layouts/ContentLayout.astro";
import BlogPostMeta from "#components/BlogPostMeta.astro";
import { BlogPost, contentfulClient } from "#lib/contentful";
import { Prism } from "#astrojs/prism";
import { runHighlighterWithAstro } from "#astrojs/prism/dist/highlighter";
export async function getStaticPaths() {
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
function extractCode(str: string): { lang: string; code: string } | null {
const match = str?.match(/```(\w+)\n([\s\S]+)\n```/);
if (match) {
return {
lang: match[1],
code: match[2],
};
}
return null;
}
const renderOptions = {
renderNode: {
[BLOCKS.EMBEDDED_ENTRY]: (node: any) => {
const { code } = node.data.target.fields;
const extractedCode = extractCode(code);
if (extractedCode) {
const { lang, code } = extractedCode;
const { classLanguage, html } = runHighlighterWithAstro(lang, code);
return `<pre class=${classLanguage} set:html=${html}></pre>`;
}
return "";
},
},
};
const pages = entries.items.map((item) => ({
params: { slug: item.fields.slug },
props: {
title: item.fields.title,
description: item.fields.description,
content: documentToHtmlString(item.fields.content, renderOptions),
date: new Date(item.fields.date).toLocaleDateString(),
},
}));
return pages;
}
const { slug } = Astro.params;
if (typeof slug !== "string") {
throw Error(`Slug should be a string. Received: ${slug}`);
}
const { description, content, title, date } = Astro.props;
---
<ContentLayout title={title} date={date}>
<BlogPostMeta
title={title}
description={description}
publishDate={date}
pagePath={`/posts/${slug}`}
slot='meta'
/>
<body>
<h1>{title}</h1>
<time>{date}</time>
<article slot='content' set:html={content} />
</body>
</ContentLayout>
I wanted to use a component, CodeBlockthat contains my styled Prism element. Unfortunately, I may be misunderstanding something about Astro, coming from React land.
---
import { Prism } from "#astrojs/prism";
const { language, code } = Astro.props;
---
<Prism lang={language} code={code} />
<style is:global>...
</style>

Pagination Apollo Client 3 - Cache merges but does not render new results when paginating

I'd love to implement nested pagination within my application. I have been reading the docs and looking at several other examples but I just can't get this to work - any help is appreciated! Thanks!
React component:
I am clicking the button to run the fetchMore function provided by the useQuery hook (apollo). The network request is going through and the new products are merged into the cache... but no new products render on the page.
export const FilterableKit = () => {
const selectedKitId = useReactiveVar(selectedKitIdVar);
const [
getKitProducts,
{ data: getKitProductsData, loading: getKitProductsLoading, fetchMore },
] = useGetKitProductsLazyQuery();
useEffect(() => {
if (selectedKitId) {
getKitProducts({
variables: {
getKitsInput: {
_id: {
string: selectedKitId,
filterBy: "OBJECTID" as StringFilterByEnum,
},
},
getProductsInput: {
config: {
pagination: {
reverse: true,
limit: 3,
},
},
},
},
});
}
}, [getKitProducts, selectedKitId]);
const kitProducts = getKitProductsData?.getKits.data?.find(
(kit) => kit?._id === selectedKitId
)?.products.data;
const handleLoadMore = () => {
if (kitProducts && kitProducts?.length > 0) {
const remaining =
getKitProductsData?.getKits.data[0]?.products.stats?.remaining;
if (remaining && remaining > 0) {
const cursor =
kitProducts[kitProducts.length - 1] &&
kitProducts[kitProducts.length - 1]?.createdAt;
fetchMore({
variables: {
getProductsInput: {
config: {
pagination: {
reverse: true,
createdAt: cursor,
},
},
},
},
});
}
}
};
return (
<CContainer>
<KitItemCards products={kitProducts} loading={getKitProductsLoading} />
<CContainer className="d-flex justify-content-center my-3">
<CButton color="primary" className="w-100" onClick={handleLoadMore}>
Load More
</CButton>
</CContainer>
</CContainer>
);
};
Type Policies: I define the "Kit" typePolicy to merge products into the correct field.
export const cache: InMemoryCache = new InMemoryCache({
typePolicies: {
Kit: {
fields: {
products: {
keyArgs: false,
merge(existing = [] as Product[], incoming: GetProductsResponse) {
if (!incoming) return existing;
if (!existing) return incoming;
const { data: products, ...rest } = incoming;
let result: any = rest;
result = [...existing, ...(products ?? [])];
return result;
},
},
},
},
});
Thanks for any pointers in the right direction! Let me know if there is something else you'd like to see.

Prisma2: How to solve n +1 Problem with Paljs

thx for any help.
Im using at the frontend the apollo-client and at the backend graphql-nexus,prisma2 and graphql-yoga server.
I want to solve the n + 1 problem with #paljs/plugins.
At the frontend I have a query posts like:
query posts{
posts {
id
favoritedBy(where: { id: { equals: $currentUserId } }) {
id
}
author {
id
avatar {
id
}
}
link {
id
}
games {
id
}
tags {
id
}
likes(where: { user: { id: { equals: $currentUserId } } }) {
id
}
}
}
Posts resolver:
import { PrismaSelect } from '#paljs/plugins'
export const posts = queryField('posts', {
type: 'Post',
list: true,
args: {
...
},
resolve: async (_parent, args, { prisma, request }, info) => {
const select = new PrismaSelect(info).value
let opArgs: FindManyPostArgs = {
take: 10,
orderBy: {
[args.orderBy]: 'desc',
},
...select
}
const post = await prisma.post.findMany(opArgs)
//The result I want to return with the "sub-models" like likes, author tags...
console.log(JSON.stringify(post, undefined, 2))
return post
},
})
I logging the queries
const prisma = new PrismaClient({
log: ['query'],
})
My Problem: With PrismaSelect, I have 5 queries more than without and If I check the request-time at the frontend I need 300-400ms longer with PrismaSelect. So what I'm doing wrong?
I saw in the #paljs/plugins doc the select in the context. Maybe that is my mistake. How can I use the select in the context?
Here ist my Context:
import { PrismaClient, PrismaClientOptions } from '#prisma/client'
import { PubSub } from 'graphql-yoga'
import { PrismaDelete, onDeleteArgs } from '#paljs/plugins'
class Prisma extends PrismaClient {
constructor(options?: PrismaClientOptions) {
super(options)
}
async onDelete(args: onDeleteArgs) {
const prismaDelete = new PrismaDelete(this)
await prismaDelete.onDelete(args)
}
}
export const prisma = new PrismaClient({
log: ['query'],
})
export const pubsub = new PubSub()
export interface Context {
prisma: PrismaClient
request: any
pubsub: PubSub
}
export function createContext(request: any): Context {
return { prisma, request, pubsub }
}
You need to know that to use my PrismaSelect plugin you need to remove the nexus-prisma-plugin package and use my Pal.js CLI to create your CRUD and ObjectType for nexus and using #paljs/nexus plugin to add in mackSchema function
import { makeSchema } from '#nexus/schema';
import * as types from './graphql';
import { paljs } from '#paljs/nexus'; // import our plugin
export const schema = makeSchema({
types,
plugins: [paljs()],// here our plugin don't use nexus-prisma-plugin
outputs: {
schema: __dirname + '/generated/schema.graphql',
typegen: __dirname + '/generated/nexus.ts',
},
typegenAutoConfig: {
sources: [
{
source: require.resolve('./context'),
alias: 'Context',
},
],
contextType: 'Context.Context',
},
});
Now add this type to your Context
export interface Context {
prisma: PrismaClient
request: any
pubsub: PubSub
select: any // here our select type
}
export function createContext(request: any): Context {
// our paljs plugin will add select object before resolver
return { prisma, request, pubsub, select: {} }
}
after you add our plugin your query will log like this
extendType({
type: 'Query',
definition(t) {
t.field('findOneUser', {
type: 'User',
nullable: true,
args: {
where: arg({
type: 'UserWhereUniqueInput',
nullable: false,
}),
},
resolve(_, { where }, { prisma, select }) {
// our plugin add select object into context for you
return prisma.user.findOne({
where,
...select,
});
},
});
},
});
Can you please try to use my pal c command to start an example from my list and try your schema and make tests with it
It is working, thx Ahmed your plugin is AWESOME!!!!!
I changed my Post-Object from
const Post = objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.authorId()
t.model.tags()
t.model.games()
t.model.link()
t.model.report()
t.model.notifications()
t.model.author()
t.model.favoritedBy({
filtering: {
id: true,
},
})
t.model.likes({
filtering: {
user: true,
},
})
}
})
to
const Post = objectType({
name: 'Post',
definition(t) {
t.string('id')
t.field('tags', {
nullable: false,
list: [true],
type: 'Tag',
resolve(parent: any) {
return parent['tags']
},
})
t.field('games', {
list: [true],
type: 'Game',
resolve(parent: any) {
return parent['games']
},
})
t.field('link', {
type: 'Link',
nullable: true,
resolve(parent: any) {
return parent['link']
},
})
t.field('notifications', {
list: [true],
type: 'Notification',
resolve(parent: any) {
return parent['notifications']
},
})
t.field('author', {
nullable: false,
type: 'User',
resolve(parent: any) {
return parent['author']
},
})
t.field('favoritedBy', {
nullable: false,
list: [true],
type: 'User',
args: {
where: 'UserWhereInput',
},
resolve(parent: any) {
return parent['favoritedBy']
},
})
t.field('likes', {
list: [true],
type: 'Like',
args: {
where: 'LikeWhereInput',
},
resolve(parent: any) {
return parent['likes']
},
})
},
})
And I also used the nexus-prisma-plugin and paljs-plugin at the same time

toODataString Method Throwing error filterOperators(...) is not a function Kendo Angular

I am using Filter widget of Kendo UI for Angular. For converting the filter Expression to oData Query I am using toODataQuery method and passing state to it.
While adding Groups I am getting
filterOperators(...) is not a function
at odata-filtering.operators.js:69
at funcs.js:4
at odata-filtering.operators.js:70
at Array.map (<anonymous>)
I have created stackblitz for the same: https://stackblitz.com/edit/angular-ivy-gny5pv?file=src%2Fapp%2Fapp.component.ts
I am able to reproduce the error but error is different.
What I am doing wrong...??
app.component.ts:
import { Component, OnInit, ElementRef, ViewChild, Output, Input, EventEmitter } from '#angular/core';
import { CompositeFilterDescriptor, FilterDescriptor, State, toODataString, normalizeFilters } from '#progress/kendo-data-query';
declare var kendo: any;
#Component({
selector: 'my-app',
template: `
<div #positionFilter></div>
`
})
export class AppComponent implements OnInit {
#ViewChild('positionFilter',{static: true}) positionFilter: ElementRef;
public filterExpression: CompositeFilterDescriptor;
public state: State = {
skip: 0,
take: 5,
// Initial filter descriptor
filter: null
};
ngOnInit() {
this.loadFilter(this.positionFilter.nativeElement, this.filterExpression);
}
public loadFilter(filterContainer, expressionVal) {
kendo.jQuery(filterContainer).kendoFilter({
dataSource: [],
//applyButton: false,
expressionPreview: true,
expression: expressionVal,
change: this.onChangeFieldDropDownEvent.bind(this),
fields: [
{ name: "ProductName",type: "string", label: "String" },
{ name: "CategoryID", type: "number", label: "Number"},
{ name: "UnitPrice", type: "boolean", label: "boolean"},
{ name: "UnitsInStock", type: "date", label: "Date" }
]
});
}
onChangeFieldDropDownEvent(e) {
console.log('Filter Expression--', e.expression);
this.addSearch();
this.filterExpression = e.expression;//.filters;
this.updateState(this.filterExpression);
let serializedFilterValue = this.serializeFilter(this.state);
console.log('serializedFilterValue----', serializedFilterValue)
}
public updateState(filter) {
this.state.filter = filter;
}
private serializeFilter(state: State): string {
return toODataString(state);
}
public addSearch() {
let container = this.positionFilter.nativeElement;
kendo.jQuery(container).find(".k-filter-field select.k-filter-dropdown").each(function (i, x) {
kendo.jQuery(x).data("kendoDropDownList").setOptions({ filter: "contains" });
});
}
}
I had raised this with Kendo Team.
This is what they suggested:
The issue occurs since the toODataString fails to serialize empty filter objects. A possible solution would be to remove all empty filters before serializing the parameters.
e.g.
private serializeFilter(state: State): string {
removeEmptyFilters(state.filter)
return toODataString(state);
}
function removeEmptyFilters(expression){
var that = this;
if (expression.filters) {
expression.filters = kendo.jQuery.grep(expression.filters, function(filter) {
removeEmptyFilters(filter);
if (filter.filters) {
return filter.filters.length;
} else {
return filter.value;
}
});
}
}

Access Scope of elements in page object model

I am working on to automate a form to create a profile. While doing that I observed a something i need to understand. Please look at the code below and if it would be great if anyone can explain the reason behind this.
I am using page object for my project.
I have selectors defined under elements and i am accessing the elements/selectors in the functions down below.
I am not able to access the elements inside the function for the below code
For the this.api.perform function when I try to access the element(subjectGenderFemale) to click on it, it errors out with the error "ERROR: Unable to locate element: "#subjectGenderFemale" using: css selector". So i had to access it with the actual selector 'input[value="F"]'. Please refer to the code below # .api.perform.
'use strict';
var assert = require('assert');
var subjectJSON = require('../../../e2e/data/subjectData.json');
module.exports = {
elements: {
newSubjectButton: {
selector: '.btn.btn--primary'
},
subjectFirstName: {
selector: 'input[name^="first_name"]'
},
subjectLastName: {
selector: 'input[name^="last_name"]'
},
subjectDateOfBirth: {
selector: 'input[name^="date_of_birth"]'
},
subjectGenderFemale: {
selector: 'input[value="F"]'
},
subjectGenderMale: {
selector: 'input[value="M"]'
},
submitButton: {
selector: '.col.col-sm-offset-2.col-sm-8>div>form>button'
}
},
commands: [{
openCreateSubjectForm: function() {
return this
.waitForElementPresent('#newSubjectButton', 1000)
//the below href needs to change to proper element
.click('a[href="/subject/create"]')
},
populateSubjectForm: function() {
return this
.setValue('#subjectFirstName', subjectJSON["createSubject"]["firstName"])
.setValue('#subjectLastName', subjectJSON["createSubject"]["lastName"])
.setValue('#subjectDateOfBirth', subjectJSON["createSubject"]["dateOfBirth"])
.api.perform(function() {
if (subjectJSON["createSubject"]["gender"]=="F") {
this.api.click('input[value="F"]')
}else if (subjectJSON["createSubject"]["gender"]=="M") {
this.api.click('input[value="M"]')
}else if (subjectJSON["createSubject"]["gender"]=="Both") {
this.api.click('input[value="Both"]')
}else {
this.api.click('input[value="No preference"]')
}
})
},
submitCreateSubjectForm: function() {
return this.click('#submitButton');
}
}]
};
i got around this problem by accessing it via this.elements.subjectGenderFemale.selector

Resources