Docs: https://serenity-js.org | Issues: https://github.com/serenity-js/serenity-js/issues | Backlog: https://github.com/orgs/serenity-js/projects
jan-molak on webdriverio
fix(webdriverio): implemented W… refactor(core): removed unused … (compare)
jan-molak on webdriverio
chore(webdriverio): run webdriv… (compare)
jan-molak on webdriverio
feat(webdriverio): initial draf… Merge branch 'master' into feat… fix(webdriverio): webdriverIONo… (compare)
renovate[bot] on chromedriver-90.x
chore(deps): update dependency … (compare)
renovate[bot] on node-14.x
chore(deps): update dependency … (compare)
renovate[bot] on node-14.x
chore(deps): update dependency … (compare)
jan-molak on gh-pages
Updates (compare)
Hey folks! I've added a neat little Question
in version 2.19.5, it's called q
and works just like string templates, but with other Serenity/JS questions:
import { q, actorCalled } from '@serenity-js/core';
import { Send, DeleteRequest } from '@serenity-js/rest';
actorCalled('Alice').attemptsTo(
Send.a(DeleteRequest.to(
q `/articles/${ Text.of(Article.id()) }`
))
)
Thoughts and feedback welcome!
Hi, is there a #result or something that can be used in the question string to display value of the question? i can only see #actor... i am trying to write a question that can be chained like tasks
class DateText extends Question {
static of(target) {
return new DateText(target);
}
as(dateFormat) {
this.dateFormat = dateFormat;
return this;
}
constructor(target) {
super();
this.target = target;
}
async performAs(actor) {
/*more fancy stuff here*/
const result = '2020/12/15';
return result;
}
}
but without toString method this throws and exception in formatter. If i return const string then it is being used in the calling Ensure as value...
Serenity/JS 2.20 has arrived with support for Cucumber 7 (including the new Rule/Example syntax) and the latest Serenity BDD reporting CLI.
There's also a brand new reporting guide on the website, and updates to the integration guide around integrating Serenity/JS with Cucumber, Jasmine, Mocha and Protractor
Take it all for a spin and let me know what you think!
Hi, how to get the parent of a Target with Serenity/JS?
Imagine two dialogs on a page, and I want to close the one with title "Awesome Title 2" it by clicking the X.
<div>
<div class="dialog">
<div class="dialog-title">Awesome Title 1</div>
<div class="dialog-content">Some information in here.</div>
<div class="close">X</div>
</div>
<div class="dialog">
<div class="dialog-title">Awesome Title 2</div>
<div class="dialog-content">Some other information in here.</div>
<div class="close">X</div>
</div>
</div>
I can target both dialogs:
const Dialogs = () => Target.all('dialogs').located(by.css('dialog'));
But I can just distinguish both dialogs by their titles.
const DialogTitle = (title: string) => Target.the(`dialog title ${title}`).located(by.cssContainingText('.dialog-title', title));
I won't use Pick
with get(index)
because the positions may change.
'Target of' won't work either because I'm not searching the child, but the parent.
I could try to use a located(by.xpath(''))
but this will be a mess in my specific case.
In Protractor there is the possibility the getDriver()
method to get the parent of an element: Protracor API
This seems not to be implemented in Serenity/JS.
I tried some things, but nothing worked out.
How to solve this? Am I missing something?
Thanks, Jan
Pick
would be an appropriate choice in your case; you could say:const DialogTitle = () => Target.the(`dialog title`).located(by.css('.dialog-title'))
const CloseButton = () => Target.the(`close button`).located(by.css('.close'))
CloseButton.of(Pick.from(Dialogs()).where(Text.of(DialogTitle()), includes('Awesome Title 1')).first())
Hi @jan-molak !
I'm having some problems with a post
method which needs an authentication token.
One previous post was suggesting to create a question in order to access the token, so I prepared my own version:
const Authorization = {
from: (
responseBody: Answerable<any> // you might want to introduce an interface to describe the response rather than `any`
) =>
Question.about(
`session token`,
actor =>
actor
.answer(responseBody)
.then(body => 'Bearer '.concat(body.access_token)) // or wherever the token is on the response body
),
}
I have a task that obtains the token:
export const RequestAuthentication = (): Task =>
Task.where(
`#actor requests auth token`,
Send.a(PostRequest.to('yyyy').with(formData)),
Log.the(LastResponse.status()),
Ensure.that(LastResponse.status(), equals(200)),
TakeNote.of(Authorization.from(LastResponse.body()))
)
And, finally, a task that performs a GET using the authorization previously obtained:
export const Search = (): Task => {
return Task.where(
`#actor searches for a case`,
Log.the(Note.of(Authorization.from(LastResponse.body()))),
Send.a(
GetRequest.to('https://xxxx').using({
headers: {
Authorization: Note.of(Authorization.from(LastResponse.body())),
},
})
),
Log.the(LastResponse.body()),
Ensure.that(LastResponse.status(), equals(200))
)
}
Since I have logged the token in a note, I can see that it is a valid one but it is not working (401 error). Maybe I need my question to return a whole AxiosRequestConfig and inject it to the using method?
Hey folks!
Quite a few people have asked me to create a dedicated Serenity/JS JUnit reporter to make it easier to integrate Serenity/JS with popular CI/CD toolchains like Jenkins CI, GitLab CI, Azure DevOps and others. It will require changes to how Serenity/JS artifacts work internally and making the mechanism more flexible to support JUnit and other custom report and artifact types in the future.
If you think that it's a worthwhile effort, and you too would like to see this feature added, please consider buying me a coffee to keep me awake while I get this done ;-)
serenity-js-cucumber-protractor-template
project might generate an empty report. This was caused by an issue with package-lock.json
file that got corrupted when Dependabot updated Serenity/JS dependencies one at a time, rather than all of them together. I've now migrated all the template projects to use RenovateApp instead of Dependabot, which seems to offer better control over what dependencies should be updated together.
hi @jan-molak , i am currently develop mobile automation using webdriverio and integrate it with serenityJS screenplay pattern,
when running the tests, so far so good (test run as expected on appium, and pass on terminal using
wdio ./config/wdio.android.conf.js/ command), i also described it on serenityJS actors and create wrapper functions around it,
but im having trouble when generating the reports, it shows zero scenario test on serenity bdd reports, can you guide me on this one?
i have a feeling that due to wdio not generate anything after my automation test fisnished(or due to serenityJS core itself).. but im not sure why,
any suggestions, hints or help will be very much appreciated, thanks in advance!
Hello again :) i am trying to verify that object property has value. There is probably existing expectation allowing to verify that, but i failed to find it and went with my own expectation. My issue is that it prints too much to the report:
Expectation.thatActualShould('exist').soThat((actualValue) => actualValue && actualValue.length < 0)
✓ user ensures that property id does exist undefined (0ms)
is there a way to rid the output of the expected value?
Hi @jan-molak
Imagine the following pseudo list:
<div class="list">
<div class="item>
<div class="header">A<div>
<div class="info">X</div>
</div>
<div class="item>
<div class="header">A<div>
<div class="info">Y</div>
</div>
<div class="item>
<div class="header">B<div>
<div class="info">X</div>
</div>
<div class="item>
<div class="header">B<div>
<div class="info">Y</div>
</div>
</div>
I would like to access an "item" but it has to match both the text values of "header" and "info".
I'd a similar question concerning this "child to parent access" here: https://gitter.im/serenity-js/Lobby?at=6013ea301ed88c58d808ad05
But somehow I was not able to manage it that way.
What I tried so far (pseudo code):
Items = () => Target.all('items').located(by.css('.items'));
ItemHeader = () => Target.the('header').located(by.css('.header'));
ItemInfo = () => Target.the('info').located(by.css('.info'));
ListItem = (header: string, info: string) =>
Pick.from<ElementFinder, ElementArrayFinder>(Items())
.where(Text.of(ItemHeader(), includes(header))
.where(Text.of(ItemInfo(), includes(info))
.first()
.describedAs(
`list entry that inlcudes '${header}' as main info and '${info}' as extra bottom info`
);
I then tried:
ItemHeader = (header: string) =>
Target.the(`main info text`).located(by.cssContainingText('.header', header));
ItemInfo = (info: string) =>
Target.the(`main info text`).located(by.cssContainingText('.info', info));
ListItem = (header: string, info: string) =>
Pick.from<ElementFinder, ElementArrayFinder>(Items())
.where(ItemHeader(header), isVisible())
.where(ItemHeader(info), isVisible())
.first()
.describedAs(
`list entry that inlcudes '${header}' as main info and '${info}' as extra bottom info`
);
This was also not successful. Only thing, that worked out was:
ListItem = (header: string, info: string) =>
Pick.from<ElementFinder, ElementArrayFinder>(Items())
.where(ItemHeader(header), isVisible())
//.where(ItemHeader(info), isVisible())
.first()
.describedAs(
`list entry that inlcudes '${header}' as main info and '${info}' as extra bottom info`
);
So, how could I "join" both of the values needed to locate my item?
I'm stuck in here. What would be the Serenity/JS solution for this problem?
Thanks in advance.
protractor-cucumber-framework
to replace its internals with Serenity/JS - protractor-cucumber-framework/protractor-cucumber-framework#198 Please feel free to upvote it if you think it's a good idea :-)
Hi @jan-molak , I've got another question: Have you (or someone else in here) ever tried to implement multiple language support to run the same spec with different user languages?
My way:
This works fine, but seems to have a lot of overhead. On the other side I need a simple way to enhance the translation list (without having the overhead in there).
What would be the Serenity/JS way to achieve this?
{
"status": "SUCCESS",
"circuitData": {
"requestTimeStamp": "2021-03-03",
"responseTimeStamp": "2021-03-03",
"circuitLst": [
{
"circuit": [
{
"circuitId": "11111",
"circuitName": "AAAAAA"
}
]
}
]
}
}
Ensure.that(
LastResponse.body<any>().map(actor => body => body.circuitData.circuitLst[0].circuit.circuitName),
contain('AAAAAA')
)
TypeError: Cannot read property 'circuitName' of undefined