How to navigate through a paginated list until find a text with Serenity-JS? - protractor

I need to assert that an element I created was added to a list, but it is added at the end of it and it is paginated.
I'm thinking of navigating through each page by calling another task this way:
export class CheckItem implements Task {
static afterCreated(): CheckItem {
return new CheckItem();
}
performAs(actor: PerformsTasks & UsesAbilities): PromiseLike<void> {
return TakeNotes.as(actor).read('item-name')
.then((itemName: string) => actor
.attemptsTo(
NavigateThroughItemList.untilFinds(itemName),
));
}
}
I need to implement NavigateThrougItemList.

Ok, I found the solution by using a recursive call. Maybe there is a better solution for this, but this one worked for me.
export class NavigateThroughItemList implements Task {
static untilFinds(itemName: string): NavigateThroughItemList {
return new NavigateThroughItemList(itemName);
}
constructor(private itemName: string) {
}
#step('{0} navigates through the list until #itemName is found')
performAs(actor: PerformsTasks): PromiseLike<void> {
return actor.attemptsTo(
See.if(Text.ofAll(ItemList.items), include(this.itemName)),
).catch(() => actor
.attemptsTo(
See.if(
WebElement.of(ItemList.nextButton),
isEnabled(),
),
)
.then(() => actor
.attemptsTo(
Click.on(ItemList.nextButton),
))
// Looping until it finds the item
.then(() => this.performAs(actor)));
}
}

Related

Why TextDocumentContentProvider dont call provideTextDocumentContent on update when query params changes?

as title says, when i wanna update TextDocumentContentProvider with different query params by calling update method provideTextDocumentContent is not called...
only way i managed to get it working was with same URI as in calling
vscode.commands.executeCommand('vscode.previewHtml', URI, 2, 'Storybook');
relevant part of code:
// calculates uri based on editor state - depends on actual caret position
// all uris will start with 'storybook://preview'
function getPreviewUri(editor: vscode.TextEditor): vscode.Uri;
// transforms uri, so web server will understand
// ex: 'storybook://preview?name=fred' -> 'http://localhost:12345/preview/fred?full=1'
function transformUri(uri: vscode.Uri): vscode.Uri;
class StorybookContentProvider implements vscode.TextDocumentContentProvider
{
provideTextDocumentContent(uri: vscode.Uri): string {
var httpUri = transformUri(uri);
return `<iframe src="${httpUri}" />`;
}
onDidChange = new vscode.EventEmitter<vscode.Uri>();
update(uri: vscode.Uri) {
this.onDidChange(uri);
}
}
export function activate(context: vscode.ExtensionContext)
{
vscode.workspace.onDidChangeTextDocument(
(e: vscode.TextDocumentChangeEvent) => {
if (e.document === vscode.window.activeTextEditor.document) {
const previewUri = getPreviewUri(vscode.window.activeTextEditor);
provider.update(previewUri);
}
}
);
vscode.window.onDidChangeTextEditorSelection(
(e: vscode.TextEditorSelectionChangeEvent) => {
if (e.textEditor === vscode.window.activeTextEditor) {
const previewUri = getPreviewUri(vscode.window.activeTextEditor);
provider.update(previewUri);
}
}
);
const provider = new StorybookContentProvider();
context.subscriptions.push(
vscode.commands.registerCommand('extension.showStorybook', () => {
vscode.commands.executeCommand('vscode.previewHtml', vscode.Uri.parse('storybook://preview'), 2, 'Storybook')
}),
vscode.workspace.registerTextDocumentContentProvider('storybook', provider)
);
}

Capture exception thrown in prototype (Typescript)

Excellent Angular 2/Material Design framework, Teradata Covalent, provides a RESTService abstract class that wraps REST api calls here:
https://teradata.github.io/covalent/#/components/http
Code to incorporate the extension is easy, as follows:
export class CustomRESTService extends RESTService<any> {
constructor(private _http: Http /* or HttpInterceptorService */) {
super(_http, {
baseUrl: 'www.api.com',
path: '/path/to/endpoint',
headers: new Headers(),
dynamicHeaders: () => new Headers(),
transform: (res: Response): any => res.json(),
});
}
}
The "update" method in the RESTService abstract class is shown here:
public update(id: string | number, obj: T, transform?: IRestTransform): Observable<any> {
let requestOptions: RequestOptionsArgs = this.buildRequestOptions();
let request: Observable<Response> = this.http.patch(this.buildUrl(id), obj, requestOptions);
return request.map((res: Response) => {
if (res.status === 200) {
if (transform) {
return transform(res);
}
return this.transform(res);
} else {
return res;
}
}).catch((error: Response) => {
return new Observable<any>((subscriber: Subscriber<any>) => {
try {
subscriber.error(this.transform(error));
} catch (err) {
subscriber.error(error);
}
});
});
}
My question is: if the update method of the abstract class throws an exception, how can that be captured in the CustomRESTService class? I.e., what Typescript code might one use to display an error in the UI?
Thank you.
First thing's first - Why would you want to catch it inside the rest client and not inside the app's logic?
Assuming you have some good reason for doing that (some other infrastructure code that you're running in the CustomRESTClient class), I would override the update function and implement error handling there.
A simple example without observables:
abstract class Base {
update(n:number):number {
return n;
}
test():bool;
}
class Child extends Base {
update(n:number):number {
return super.update(n)*2;
}
test():bool {
return true;
}
}

How to use node-simple-schema reactively?

Given that there is not much examples about this, I am following the docs as best as I can, but the validation is not reactive.
I declare a schema :
import { Tracker } from 'meteor/tracker';
import SimpleSchema from 'simpl-schema';
export const modelSchema = new SimpleSchema({
foo: {
type: String,
custom() {
setTimeout(() => {
this.addValidationErrors([{ name: 'foo', type: 'notUnique' }]);
}, 100); // simulate async
return false;
}
}
}, {
tracker: Tracker
});
then I use this schema in my component :
export default class InventoryItemForm extends TrackerReact(Component) {
constructor(props) {
super(props);
this.validation = modelSchema.newContext();
this.state = {
isValid: this.validation.isValid()
};
}
...
render() {
...
const errors = this.validation._validationErrors;
return (
...
)
}
}
So, whenever I try to validate foo, the asynchronous' custom function is called, and the proper addValidationErrors function is called, but the component is never re-rendered when this.validation.isValid() is supposed to be false.
What am I missing?
There are actually two errors in your code. Firstly this.addValidationErrors cannot be used asynchronously inside custom validation, as it does not refer to the correct validation context. Secondly, TrackerReact only registers reactive data sources (such as .isValid) inside the render function, so it's not sufficient to only access _validationErrors in it. Thus to get it working you need to use a named validation context, and call isValid in the render function (or some other function called by it) like this:
in the validation
custom() {
setTimeout(() => {
modelSchema.namedContext().addValidationErrors([
{ name: 'foo', type: 'notUnique' }
]);
}, 100);
}
the component
export default class InventoryItemForm extends TrackerReact(Component) {
constructor(props) {
super(props);
this.validation = modelSchema.namedContext();
}
render() {
let errors = [];
if (!this.validation.isValid()) {
errors = this.validation._validationErrors;
}
return (
...
)
}
}
See more about asynchronous validation here.

Angular 2 data service

I'm building an observable data service based on the following article: https://coryrylan.com/blog/angular-2-observable-data-services
In the article he used an array as an example, here I will use the user object since I'm developing the user service.
Here's what I got:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Events, SqlStorage, Storage } from 'ionic-angular';
import { Subject } from 'rxjs/Subject';
export interface DataStore {
user: Object
}
#Injectable()
export class UserService {
private baseUrl: string;
private storage: Storage;
private _user$: Subject<Object>;
private dataStore: DataStore;
constructor(
private http: Http
) {
this.baseUrl = 'http://localhost:3000';
this.storage = new Storage(SqlStorage);
this._user$ = <Subject<Object>>new Subject();
this.dataStore = {
user: { name: '' }
};
}
set user$(user: Object) {
this.storage.set('user', JSON.stringify(user));
this.dataStore.user = user;
this._user$.next(this.dataStore.user);
}
get user$() {
return this._user$.asObservable();
}
loadUser() {
return this.storage.get('user').then(
((user: string): Object => {
this.dataStore.user = JSON.parse(user);
this._user$.next(this.dataStore.user);
return this.dataStore.user;
})
);
}
login(accessToken: string) {
return this.http
.post('http://localhost:3000/login', { access_token: accessToken })
.retry(2)
.map((res: Response): any => res.json());
}
logout(): void {
this.storage.remove('user');
}
}
To authenticate I call the login() function and set the user data if everything ok.
this.userService.login(this.data.accessToken)
.subscribe(
(user: Object) => {
this.userService.user$ = user;
this.nav.setRoot(EventListComponent);
},
(error: Object) => console.log(error)
);
I feel it is better set the user data inside the service. I could do the following:
login(accessToken: string) {
return this.http
.post('http://localhost:3000/login', {
access_token: accessToken
})
.retry(2)
.map((res: Response): any => res.json())
.subscribe(
(user: Object) => {
this.userService.user$ = user;
this.nav.setRoot(EventListComponent);
},
(error: Object) => console.log(error)
);
}
But I won't be able to subscribe to the login() function in the component since it's already subscribed. How could I redirect the user if everything ok or show an alert if anything goes wrong in the component but setting the user inside the service?
In the main component I load the user data and set the rootPage:
this.userService.loadUser().then(
(user: Object) => this.rootPage = EventListComponent,
(error: Object) => this.rootPage = LoginComponent
);
I thought that calling the loadUser() function at this time I would not have to call it again, but I have to call it in all components that I need the user data:
this.user = this.userService.user$;
this.userService.loadUser();
I don't think the service is the way it should, what could I improve? Is there any better way to achieve what I want? Any example or idea?

Rx: Aggregate IObservable<...> to IObservable<Unit>

I have an observable of type IObservable<...> and need to return from the function an observable of type IObservable<Unit>. Currently I do it as follows:
IObservable<Unit> DoSomething() {
var observable = SomeOtherMethodThatReturnsObservable();
// convert Convert IObservable<...> to IObservable<Unit>
var ret = new AsyncSubject<Unit>();
observable.Subscribe(_ => { }, ret.OnCompleted);
return ret;
}
Is there more nice way of doing so? Maybe extension method or something?
I would not use the example you gave in production, because it does not consider the following:
What happens if the Observable errors?
The error is eaten, and the consumer never knows the observable has ended.
What happens if the consumer disposes of it's subscription?
The underlying subscription is not disposed of
What happens if the consumer never subscribes?
The underlying observable is still subscribed to.
Typically, using the built in operators provide the best implementation.
Here's an "Rx-y" way of doing it.
source.IgnoreElements()
.Cast<Unit>()
.Concat(Observable.Return(Unit.Default));
Here's another a way to do the same thing: :)
without built-in-operators. This is arguably more efficient, but theres not real gain here.
// .Cast<Unit>()
Observable.Create<Unit>(o => { // Also make sure you return the subscription!
return source.Subscribe(
// .IgnoreElements()
_ => { },
// Make sure errors get passed through!!!
o.OnError,
// .Concat(Observable.Return(Unit.Default));
() => {
o.OnNext(Unit.Default);
o.OnCompleted();
});
});
Here's how to write it just once.
public static class ObsEx
{
public static IObservable<Unit> WhenDone<T>(this IObservable<T> source)
{
return Observable.Create<Unit>(observer =>
{
return source.Subscribe(
_ => { },
observer.OnError,
() => {
observer.OnNext(Unit.Default);
observer.OnCompleted();
});
});
}
}
And use it like so: source.WhenDone();