Check google reCaptcha service is on or off - user-interface

I am using simple google recaptcha.
My requirement is that if google api is not available (i.e. if google server is down, know its not usual case) means not getting any reply from google server then while loding the form I will hide the google reCaptcha wrapper and while submitting the form I don't want to validate google recaptcha.
Please suggest How can I achieve this.

Google does not provide that data (assuming they are always up).
But you could go about it this way. Dynamically load the script and check for the event existence in the callback. If no event is available then it failed.
Check out the #example comment for usage.
var setAttributes = function (el, attrs) {
/**
* #method simple for in loop to help with creating elements programatically
* #param {object} el - HTMLElement attributes are getting added to
* #param {object} attrs - object literal with key/values for desired attributes
* #example setAttributes(info,{
* 'id' : 'info'
* 'class' : 'my-class-name'
* });
*/
'use strict';
var key;
for (key in attrs) {
if (attrs.hasOwnProperty(key)) {
el.setAttribute(key, attrs[key]);
}
}
return el;
};
var getScript = function (url, fullPath) {
/**
* #method dynamically add script tags to the page.
* #param {url} string with relative path and file name - do not include extension
* #param {fullPath} string with absolute path
* #example getScript('FrameAdjustChild');
* #example getScript('','https://www.google-analytics.com/analytics.js');
*/
'use strict';
var setAtt, PATH = /js/, /* or wherever you keep your scripts */
el = document.createElement('script'),
attrs = {
defer: true,
src: null,
type: 'text/javascript'
};
/** look for a string based, protocol agnostic, js file url */
if (typeof fullPath === 'string' && fullPath.indexOf('http') === 0) {
attrs.src = fullPath;
}
/** look for any string with at least 1 character and prefix our root js dir, then append extension */
if (typeof url === 'string' && url.length >= 1) {
attrs.src = PATH + url + '.js';
}
setAtt = setAttributes(el,attrs);
el.addEventListener('load', function (event) {
if (event) {
/* status is good */
}
else {
/* status is bad */
}
}, false);
document.body.appendChild(el);
return el;
};

Related

Cypress should not.exist or not.be.visible

Because of - imo - poor page design, I've found myself having problems verify the visibility or non-existance of one or more elements on a page.
The problem is that some of the elements does not exist, while some of them have CSS property display:none. But the existing test code checks for not.exist, which makes the test fail. But I cannot change to not.be.visible, since then it will fail on the other elements.
So: Is it possible to do an OR in an assertion? Somthing like
cy.get('blabla').should('not.be.visible').or.cy.get('blabla').should('not.exist');
The above line compiles, but yields an undefined on the second part, so it doesn't work.
Here's the code:
(I don't consider the code architecture important - the question is basically the OR thing.)
page.sjekkAtDellaanFelterVises(2, 2, [
DellaanFelter.formaal,
DellaanFelter.opprinneligLaanebelop,
DellaanFelter.utbetalingsdato,
DellaanFelter.restlaanInnfridd,
]);
public sjekkAtDellaanFelterVisesALT(sakRad: number, delLanRad: number, felter: DellaanFelter[]) {
this.sjekkFelter(felter, DellaanFelter, (felt: string) => this.delLanAccordionBody(sakRad, delLanRad).get(this.e2e(felt)));
}
#ts-ignore
public sjekkFelterALT<T, E extends Node = HTMLElement>(felter: T[], enumType, lookupFn: (felt: string) => Chainable<JQuery<E>>) {
this.valuesOfEnum(enumType).forEach(felt => {
this.sjekkFelt(felt, felter, enumType, lookupFn);
});
}
// #ts-ignore enumType fungerer fint i praksis ...
public sjekkFeltALT<T, E extends Node = HTMLElement>(felt: string, felter: T[], enumType, lookupFn: (felt: string) => Chainable<JQuery<E>>) {
if (felter.some(feltSomSkalVises => enumType[feltSomSkalVises] == felt)) {
lookupFn(felt).should('be.visible');
} else {
lookupFn(felt).should('not.exist');
}
}
Or is the solution to try and check if the elements exists first, then if they do, check the visibility?
The tl;dr is that there isn't going to be a simple solution here -- Cypress' get command has assertions, so you can't easily catch or eat those exceptions. If you try to get an element that doesn't exist, Cypress will have a failed assertion. Unfortunately, the best case would be to have deterministic behavior for each assertion.
More info on why Cypress behaves this way here.
I think your best case for doing this would be to write a custom Chai assertion, but I don't have any experience in doing anything like that. Here is Chai's documentation on doing so.
If you wanted to simplify your code, but knew which elements should not exist and which elements should not be visible, you could write a custom command to handle that.
Cypress.Commands.add('notExistOrNotVisible', (selector, isNotExist) => {
cy.get(selector).should(isNotExist ? 'not.exist' : 'not.be.visible');
});
cy.notExistOrNotVisible('foo', true); // asserts that `foo` does not exist
cy.notExistOrNotVisible('bar', false); // asserts that `bar` is not visible
I arbitrarily made not exist the positive case, but you could switch that and the logic in the should.
I was facing the same problem, with some modals being destroyed (i.e. removed from the DOM) on close and others being just hidden.
I found a way to kinda emulate an or by adding the visibility check as a filter to the selection, then asserting non-existence:
cy.get('my-selector').filter(':visible').should('not.exist')
The error messages in case of failure are not as self-explanatory ("expected :visible to not exist") and you have to read the log a bit further to understand. If you don't need the separation between selector and filter you can combine the both to make get a nicer error message ("expected my-selector:visible to not exist"):
cy.get('my-selector:visible').should('not.exist')
Hopefully this will help some of you. I've been working with Cypress for a while now and found these particular custom commands to be pretty useful. I hope they help you too. ( Check for visibility utilizes the checkExistence command as well. You can just use the cy.isVisible() command and it will automatically check if it's at least in the DOM before continuing ).
P.S. These commands are still being tweaked - be nice :)
Command for checking existence
/**
* #param {String} errorMessage The error message you want to throw for the function if no arg is used
*/
const isRequired = (errorMessage = '--- Parameter is required! ---') => { throw new Error(errorMessage); };
/**
* #description Check if an element, found through xpath selector, exists or not in the DOM
* #param {String} xpath Required. Xpath to pass into the function to check if it exists in the DOM
* #param {Object} ifFound Message to pass to cy.log() if found. Default message provided
* #param {String} ifNotFound Message to pass to cy.log() if not found. Default message provided
* #returns Boolean. True if found, False if not found
* #example cy.checkExistence("xpath here", "Found it!", "Did not find it!").then(result=>{if(result==true){ // do something } else { // do something else }})
*/
Cypress.Commands.add('checkExistence',(xpath=isRequired(), ifFound, ifNotFound )=>{
return cy.window().then((win) => {
return (function(){
let result = win.document.evaluate(xpath, win.document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
if(result!=null && result!=undefined){
if ( ifFound != undefined ) {
cy.log( `_** ${ifFound} **_` );
}
return cy.wrap( true );
} else {
if ( ifNotFound != undefined ) {
cy.log( `_** ${ifNotFound} **_` );
}
return cy.wrap( false );
}
})();
});
})
Command for checking visibility
/**
* #param {String} errorMessage The error message you want to throw for the function if no arg is used
*/
const isRequired = (errorMessage = '--- Parameter is required! ---') => { throw new Error(errorMessage); };
/**
* #description Check to see if an element is visible or not, using xpath selector or JQuery element
* #param {String} Xpath Xpath string
* #param {Boolean} waitForVisibility Optional. False by default. Set to true to wait for element to become visible.
* #param {Number} timeout Optional. Timeout for waitForVisibility param.
* #returns Boolean. True if visible, False if not visible
* #example cy.isVisible("//div").then(result=>{})
*/
Cypress.Commands.add('isVisible', (Xpath=isRequired(), waitForVisibility=false, timeout)=>{
cy.checkExistence(Xpath).then(result=>{
if(result==true){
cy.xpath(Xpath).then($element => {
if ($element.is(':visible')){
return cy.wrap(true)
} else {
if(waitForVisibility===true){
if(!timeout){
cy.logSpecial('wave',3, `Must provide value for timeout. Recieved ${timeout}`, true)
} else {
cy.log(`Waiting for element to become visible within ${timeout / 1000} seconds...`, true).then(()=>{
let accrued;
let interval = 250;
(function retry(){
if ($element.is(':visible')){
return cy.wrap(true)
} else {
accrued = accrued + interval;
if(accrued>=timeout){
cy.log(`Timeout waiting for element to become visible. Waited ${timeout / 1000} seconds.`)
return cy.wrap(false)
} else {
cy.wait(interval)
cy.wrap(retry())
}
}
})();
})
}
} else {
return cy.wrap(false)
}
}
})
} else {
cy.log(`Element does not exist in the DOM. Skipping visibility check...`).then(()=>{
if(throwError==true){
throw new Error (`Element of xpath ${Xpath} was not visible.`)
}
return cy.wrap(false)
})
}
})
})

Sorting does not work for the customized ProductListComponentService.(CustomProductListComponentService)

Spartacus lists 10 products in the pagination defualt on its product listing page. But I wanted it to be 12.
In my .ts file normally for PLP
protected productListComponentServiceDefault: ProductListComponentService,
I was using the structure.
but I created "CutomProductListComponentService" by customizing the "ProductListComponentService" file.
protected defaultPageSize = 12;
I did what I wanted by adding. Pagination artok 12 works.
But after this customization, the "SORTING" operation within the page does not work anymore.
Inside CutomProductListComponentService I added the following:
import { Injectable } from '#angular/core';
import { ActivatedRoute, Router } from '#angular/router';
import {
ActivatedRouterStateSnapshot,
CurrencyService,
LanguageService,
ProductSearchPage,
ProductSearchService,
RouterState,
RoutingService,
} from '#spartacus/core';
import { combineLatest, Observable, using } from 'rxjs';
import {
debounceTime,
distinctUntilChanged,
filter,
map,
shareReplay,
tap,
} from 'rxjs/operators';
// #ts-ignore
import { ProductListRouteParams, SearchCriteria } from './product-list.model';
/**
* The `ProductListComponentService` is used to search products. The service is used
* on the Product Listing Page, for listing products and the facet navigation.
*
* The service exposes the product search results based on the category and search
* route parameters. The route parameters are used to query products by the help of
* the `ProductSearchService`.
*/
#Injectable({ providedIn: 'root' })
export class CustomProductListComponentService {
// TODO: make it configurable
protected defaultPageSize = 12;
protected readonly RELEVANCE_ALLCATEGORIES = ':relevance:allCategories:';
constructor(
protected productSearchService: ProductSearchService,
protected routing: RoutingService,
protected activatedRoute: ActivatedRoute,
protected currencyService: CurrencyService,
protected languageService: LanguageService,
protected router: Router
) {}
/**
* Emits the search results for the current search query.
*
* The `searchResults$` is _not_ concerned with querying, it only observes the
* `productSearchService.getResults()`
*/
protected searchResults$: Observable<
ProductSearchPage
> = this.productSearchService
.getResults()
.pipe(filter((searchResult) => Object.keys(searchResult).length > 0));
/**
* Observes the route and performs a search on each route change.
*
* Context changes, such as language and currencies are also taken
* into account, so that the search is performed again.
*/
protected searchByRouting$: Observable<
ActivatedRouterStateSnapshot
> = combineLatest([
this.routing.getRouterState().pipe(
distinctUntilChanged((x, y) => {
// router emits new value also when the anticipated `nextState` changes
// but we want to perform search only when current url changes
return x.state.url === y.state.url;
})
),
...this.siteContext,
]).pipe(
debounceTime(0),
map(([routerState, ..._context]) => (routerState as RouterState).state),
tap((state: ActivatedRouterStateSnapshot) => {
const criteria = this.getCriteriaFromRoute(
state.params,
state.queryParams
);
this.search(criteria);
})
);
/**
* This stream is used for the Product Listing and Product Facets.
*
* It not only emits search results, but also performs a search on every change
* of the route (i.e. route params or query params).
*
* When a user leaves the PLP route, the PLP component unsubscribes from this stream
* so no longer the search is performed on route change.
*/
readonly model$: Observable<ProductSearchPage> = using(
() => this.searchByRouting$.subscribe(),
() => this.searchResults$
).pipe(shareReplay({ bufferSize: 1, refCount: true }));
/**
* Expose the `SearchCriteria`. The search criteria are driven by the route parameters.
*
* This search route configuration is not yet configurable
* (see https://github.com/SAP/spartacus/issues/7191).
*/
protected getCriteriaFromRoute(
routeParams: ProductListRouteParams,
queryParams: SearchCriteria
): SearchCriteria {
return {
query: queryParams.query || this.getQueryFromRouteParams(routeParams),
pageSize: queryParams.pageSize || this.defaultPageSize,
currentPage: queryParams.currentPage,
sortCode: queryParams.sortCode,
};
}
/**
* Resolves the search query from the given `ProductListRouteParams`.
*/
protected getQueryFromRouteParams({
query,
categoryCode,
brandCode,
}: ProductListRouteParams) {
if (query) {
return query;
}
if (categoryCode) {
return this.RELEVANCE_ALLCATEGORIES + categoryCode;
}
// TODO: drop support for brands as they should be treated
// similarly as any category.
if (brandCode) {
return this.RELEVANCE_ALLCATEGORIES + brandCode;
}
}
/**
* Performs a search based on the given search criteria.
*
* The search is delegated to the `ProductSearchService`.
*/
protected search(criteria: SearchCriteria): void {
const currentPage = criteria.currentPage;
const pageSize = criteria.pageSize;
const sort = criteria.sortCode;
this.productSearchService.search(
criteria.query,
// TODO: consider dropping this complex passing of cleaned object
Object.assign(
{},
currentPage && { currentPage },
pageSize && { pageSize },
sort && { sort }
)
);
}
/**
* Get items from a given page without using navigation
*/
getPageItems(pageNumber: number): void {
this.routing
.getRouterState()
.subscribe((route) => {
const routeCriteria = this.getCriteriaFromRoute(
route.state.params,
route.state.queryParams
);
const criteria = {
...routeCriteria,
currentPage: pageNumber,
};
this.search(criteria);
})
.unsubscribe();
}
/**
* Sort the search results by the given sort code.
*/
sort(sortCode: string): void {
this.route({ sortCode });
}
/**
* Routes to the next product listing page, using the given `queryParams`. The
* `queryParams` support sorting, pagination and querying.
*
* The `queryParams` are delegated to the Angular router `NavigationExtras`.
*/
protected route(queryParams: SearchCriteria): void {
this.router.navigate([], {
queryParams,
queryParamsHandling: 'merge',
relativeTo: this.activatedRoute,
});
}
/**
* The site context is used to update the search query in case of a
* changing context. The context will typically influence the search data.
*
* We keep this private for now, as we're likely refactoring this in the next
* major version.
*/
private get siteContext(): Observable<string>[] {
// TODO: we should refactor this so that custom context will be taken
// into account automatically. Ideally, we drop the specific context
// from the constructor, and query a ContextService for all contexts.
return [this.languageService.getActive(), this.currencyService.getActive()];
}
}
I call the Sort function in plp.ts as follows.
constructor(
protected productListComponentService: CustomProductListComponentService,
) {}
sortList(sortCode: string): void {
this.productListComponentService.sort(sortCode);
}
Sort process doesn't work. Can you help me? Thank you very much in advance.
You also need to replace "ProductListComponentService" to "CustomProductListComponentService" in "ProductFacetService".
The better way to do this is:
In providers of "ProductListModule", add:
{
provide: ProductListComponentService,
useClass: CustomProductListComponentService,
},

Firestore pagination - Is there any query compatible with firebase's limitToLast?

Is there a way to implement back pagination with firestore?
I am struggling to implement pagination with firestore, and there are limited firestore queries for it. Forward pagination can be made by startAt and limit method, that is ok. But back pagination can't be easily done, because we only have endBefore, and endAt method, and how can we get last n elements from given document? I know realtime database have method limitToLast. Is there any query like this for firestore? (Also I need to implement multiple sorting, so getting last documents with "ASC" or "DESC" sorting will not work)
Help much appreciated.
Thanks!
The equivalent to the limitToLast(...) operation from the Firebase Realtime Database in Cloud Firestore is to order the data descending (which is possible in Firestore) and then just limit(...). If you're having problems implement this, update your question to show what you've done.
I agree that this is a sub-optimal API for back-pagination, since you're receiving the items in reverse order.
Simpler answer: Firestore now has .limitToLast(), which works exactly as you think it does. Used in my own (guess I need to publish it soon) Firestore Wrapper:
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// *** Paginate API ***
export const PAGINATE_INIT = 0;
export const PAGINATE_PENDING = -1;
export const PAGINATE_UPDATED = 1;
export const PAGINATE_DEFAULT = 10;
export const PAGINATE_CHOICES = [10, 25, 50, 100, 250, 500];
/**
* #classdesc
* An object to allow for paginating a table read from Firestore. REQUIRES a sorting choice
* #property {Query} Query that forms basis for the table read
* #property {number} limit page size
* #property {QuerySnapshot} snapshot last successful snapshot/page fetched
* #property {enum} status status of pagination object
* #method PageForward pages the fetch forward
* #method PageBack pages the fetch backward
*/
export class PaginateFetch {
Query = null;
limit = PAGINATE_DEFAULT;
snapshot = null;
status = null; // -1 pending; 0 uninitialize; 1 updated;
/**
* ----------------------------------------------------------------------
* #constructs PaginateFetch constructs an object to paginate through large
* Firestore Tables
* #param {string} table a properly formatted string representing the requested collection
* - always an ODD number of elements
* #param {array} filterArray an (optional) 3xn array of filter(i.e. "where") conditions
* #param {array} sortArray a 2xn array of sort (i.e. "orderBy") conditions
* #param {ref} ref (optional) allows "table" parameter to reference a sub-collection
* of an existing document reference (I use a LOT of structered collections)
*
* The array is assumed to be sorted in the correct order -
* i.e. filterArray[0] is added first; filterArray[length-1] last
* returns data as an array of objects (not dissimilar to Redux State objects)
* with both the documentID and documentReference added as fields.
* #param {number} limit (optional)
* #returns {PaginateFetchObject}
**********************************************************************/
constructor(
table,
filterArray = null,
sortArray = null,
ref = null,
limit = PAGINATE_DEFAULT
) {
const db = ref ? ref : fdb;
this.limit = limit;
this.Query = sortQuery(
filterQuery(db.collection(table), filterArray),
sortArray
);
this.status = PAGINATE_INIT;
}
/**
* #method Page
* #returns Promise of a QuerySnapshot
*/
PageForward = () => {
const runQuery = this.snapshot
? this.Query.startAfter(_.last(this.snapshot.docs))
: this.Query;
this.status = PAGINATE_PENDING;
return runQuery
.limit(this.limit)
.get()
.then((QuerySnapshot) => {
this.status = PAGINATE_UPDATED;
//*IF* documents (i.e. haven't gone beyond start)
if (!QuerySnapshot.empty) {
//then update document set, and execute callback
//return Promise.resolve(QuerySnapshot);
this.snapshot = QuerySnapshot;
}
return this.snapshot.docs.map((doc) => {
return {
...doc.data(),
Id: doc.id,
ref: doc.ref
};
});
});
};
PageBack = () => {
const runQuery = this.snapshot
? this.Query.endBefore(this.snapshot.docs[0])
: this.Query;
this.status = PAGINATE_PENDING;
return runQuery
.limitToLast(this.limit)
.get()
.then((QuerySnapshot) => {
this.status = PAGINATE_UPDATED;
//*IF* documents (i.e. haven't gone back ebfore start)
if (!QuerySnapshot.empty) {
//then update document set, and execute callback
this.snapshot = QuerySnapshot;
}
return this.snapshot.docs.map((doc) => {
return {
...doc.data(),
Id: doc.id,
ref: doc.ref
};
});
});
};
}
/**
* ----------------------------------------------------------------------
* #function filterQuery
* builds and returns a query built from an array of filter (i.e. "where")
* consitions
* #param {Query} query collectionReference or Query to build filter upong
* #param {array} filterArray an (optional) 3xn array of filter(i.e. "where") conditions
* #returns Firestor Query object
*/
export const filterQuery = (query, filterArray = null) => {
return filterArray
? filterArray.reduce((accQuery, filter) => {
return accQuery.where(filter.fieldRef, filter.opStr, filter.value);
}, query)
: query;
};
/**
* ----------------------------------------------------------------------
* #function sortQuery
* builds and returns a query built from an array of filter (i.e. "where")
* consitions
* #param {Query} query collectionReference or Query to build filter upong
* #param {array} sortArray an (optional) 2xn array of sort (i.e. "orderBy") conditions
* #returns Firestor Query object
*/
export const sortQuery = (query, sortArray = null) => {
return sortArray
? sortArray.reduce((accQuery, sortEntry) => {
return accQuery.orderBy(sortEntry.fieldRef, sortEntry.dirStr || "asc");
//note "||" - if dirStr is not present(i.e. falsy) default to "asc"
}, query)
: query;
};
I also have the equivalent for CollectionGroup queries, and listeners for each as well.
I was running into this same issue, and not understanding why using limit with endAt wasn't returning the results I desired. I was attempting to implement a list in which you could paginate in both directions, first forward and then backward back to the start of the list.
To remedy the situation I decided to just cache the startAfter DocumentSnapshot for each page so that one can move both directions, in this way I never have to use endAt. The only time this will become an issue is if the collection of documents shifts or changes while the user is on a page other than the first page, but by returning to the first page it will reset to the beginning of the collection.
Yes. Building upon Frank's answer...
Have something like this in your query...
if (this.next) {
// if next, orderBy field descending, start after last field
q.orderBy('field', 'desc');
q.startAfter(this.marker);
} else if (this.prev) {
// if prev, orderBy field ascending, start after first field
q.orderBy('field', 'asc');
q.startAfter(this.marker);
} else {
// otherwise just display first page results normally
q.orderBy('field', 'desc');
}
q.limit(this.pageSize);
and then reverse it when you get the query...
this.testsCollection
.valueChanges({ idField: 'id' })
.pipe(
tap(results => {
if (this.prev) {
// if previous, need to reverse the results...
results.reverse();
}
})
)
I just want to share my code for Firestore pagination.
I am using react hooks w/ NextJS.
You will need to have "useFirestoreQuery" hook, which can be found here.
https://usehooks.com/useFirestoreQuery/
So here is my set up.
/* Context User */
const {user} = useUser()
/* States */
const [query, setQuery] = useState(null)
const [ref, setRef] = useState(null)
const [reverse, setReverse] = useState(false)
const [limit, setLimit] = useState(2)
const [lastID, setLastID] = useState(null)
const [firstID, setFirstID] = useState(null)
const [page, setPage] = useState(1)
/* Query Hook */
const fireCollection = useFirestoreQuery(query)
/* Set Ref, **When firebase initialized** */
useEffect(() => {
user?.uid &&
setRef(
firebase
.firestore()
.collection('products')
.where('type', '==', 'vaporizers')
)
}, [user])
/* Initial Query, **When ref set** */
useEffect(() => {
ref && setQuery(ref.orderBy('id', 'asc').limit(limit))
}, [ref])
/* Next Page */
const nextPage = useCallback(() => {
setPage((p) => parseInt(p) + 1)
setReverse(false)
setQuery(ref.orderBy('id', 'asc').startAfter(lastID).limit(limit))
}, [lastID, limit])
/* Prev Page */
const prevPage = useCallback(() => {
setPage((p) => parseInt(p) - 1)
setReverse(true)
setQuery(ref.orderBy('id', 'desc').startAfter(firstID).limit(limit))
}, [firstID, limit])
/* Product List */
const ProductList = ({fireCollection}) => {
const [products, setProducts] = useState([])
useEffect(() => {
let tempProducts = []
let tempIDs = []
const {data} = fireCollection
for (const key in data) {
const product = data[key]
tempIDs.push(product.id)
tempProducts.push(<ProductRow {...{product}} key={key} />)
}
if (reverse) {
tempProducts.reverse()
tempIDs.reverse()
}
setFirstID(tempIDs[0])
setLastID(tempIDs.pop())
setProducts(tempProducts)
}, [fireCollection])
return products
}
I moved the 'ProductList' outside of the component with a context provider, but this is the gist of it.
Note.
If you are looking for the total number of products. I suggest you keep up with the totals with these cloud functions. You will need to store your totals in a separate collection. I call mine 'shortcuts'.
exports.incrementProducts = functions.firestore
.document('products/{id}')
.onCreate(async (snap, context) => {
const createdProduct = snap.data()
/* Increment a shortcut collection that holds the totals to your products */
})
exports.decrementProducts = functions.firestore
.document('products/{id}')
.onDelete((snap, context) => {
const deletedProduct = snap.data()
/* Decrement a shortcut collection that holds the totals to your products */
})
Don't Forget
Make sure you set your indexes for all this to work. Here is what mine looks like.

Cannot create async method which return ElementArrayFinder for E2E tests

I have a class where I want to make a async method which will return ElementArrayFinder. I need exactly this type, not ElementFinder[] because my waiters based on it and I can wait element which isn't present now in DOM. Below you can find a simple example of the structure. At row 'return this.collection;' i have an error:
[ts]
Type 'any[]' is not assignable to type 'ElementArrayFinder'.
Property 'browser_' is missing in type 'any[]'.
I tried to cast result in a different way and tried to use Promise.resolve, but no success. Could somebody help me with this case?
export class Test {
private grid1: ElementFinder;
private grid2: ElementFinder;
get collection1(): ElementArrayFinder {
return this.grid1
.element(by.css('tbody'))
.all(by.tagName('tr'));
}
get collection2(): ElementArrayFinder {
return this.grid2
.element(by.css('tbody'))
.all(by.tagName('tr'));
}
public async getCollection(): Promise<ElementArrayFinder> {
if (await this.collection.count() === 0) {
return this.collection1;
}
return this.collection2;
}
}
Bug report
Node Version: v8.2.1
Protractor Version: 5.2.0
I found the answer in file element.d.ts file.
/**
* Retrieve the elements represented by the ElementArrayFinder. The input
* function is passed to the resulting promise, which resolves to an
* array of ElementFinders.
*
* #alias element.all(locator).then(thenFunction)
* #view
* <ul class="items">
* <li>First</li>
* <li>Second</li>
* <li>Third</li>
* </ul>
*
* #example
* element.all(by.css('.items li')).then(function(arr) {
* expect(arr.length).toEqual(3);
* });
*
* // Or using the shortcut $$() notation instead of element.all(by.css()):
*
* $$('.items li').then(function(arr) {
* expect(arr.length).toEqual(3);
* });
*
* #param {function(Array.<ElementFinder>)} fn
* #param {function(Error)} errorFn
*
* #returns {!webdriver.promise.Promise} A promise which will resolve to
* an array of ElementFinders represented by the ElementArrayFinder.
*/
then<T>(fn?: (value: ElementFinder[] | any[]) => T | wdpromise.IThenable<T>, errorFn?: (error: any) => any): wdpromise.Promise<T>;
Based on this documentation ElementArrayFinder type is resolved by .then to the ElementFinder[]. That's why I cannot create method which return ElementArrayFinder type in promise, but can create method which simply return ElementArrayFinder type.

Is there a function like _compile_select or get_compiled_select()?

Looks like _compile_select is deprecated and get_compiled_select is not added to 2.1.0. Are there any other functions like those two? And also I am curious. Is there any particular reason to not adding get_compiled_select() to Active Record and removing _compile_select?
I've added get_compiled_select() to DB_active_rec.php and it seems to work without problem, but i wouldn't remove _compile_select() since it's used in many other methods.
The pull request for adding this method is here, with some other useful methods like:
get_compiled_select()
get_compiled_insert()
get_compiled_update()
get_compiled_delete()
https://github.com/EllisLab/CodeIgniter/pull/307
if you want just the method, it's just this:
/**
* Get SELECT query string
*
* Compiles a SELECT query string and returns the sql.
*
* #access public
* #param string the table name to select from (optional)
* #param boolean TRUE: resets AR values; FALSE: leave AR vaules alone
* #return string
*/
public function get_compiled_select($table = '', $reset = TRUE)
{
if ($table != '')
{
$this->_track_aliases($table);
$this->from($table);
}
$select = $this->_compile_select();
if ($reset === TRUE)
{
$this->_reset_select();
}
return $select;
}

Resources