I'm trying to write assertions on arrays containing several DivHtmlElements (angular).
toMatchObject function in the Jest documentation allows to test array content based on element partial matching; but it requires to know the inner structures of each element...
Based on expect.objectContaining(object) matcher (which also requires to know about tested object inner structure), it would be nice to have a more generic matcher available; something like expect.objectMatching((object: any) => boolean) or like expect.objectMatching(jestMatcher).
It could be used like this:
const fixture: ComponentFixture<MyComponent> = getFixtureUnderTest();
fixture.detectChanges();
const divs = fixture.debugElement.queryAll(By.css('.flex-row'));
expect(divs).toMatchObject([
expect.objectMatching((htmlElement) => htmlElement.nativeElement.textContent === 'foo'),
expect.objectMatching((htmlElement) => htmlElement.nativeElement.getAttribute('class').includes('badge-info-light'))
]);
Or is there another simple way to write such assertions?
Related
In a parent component I have something like:
render() => {
const data = {a:1,b:[1,2,3]}; // of course this is a simplified version of the code
return html`<child-component data=${data}></child-component>`
}
Which is basically equivalent to:
render() => {
const data = {a:1,b:[1,2,3]}; // of course this is a simplified version of the code
return html`<child-component data="[object Object]"></child-component>`
}
Which is basically useless...
Is there a simple way to pass complex object hierarchies into litElement components?
As far as I can tell, my options are:
Option 1. Use attributes: I'm a bit of a litElement noob so I'm not sure if this will work and I'm not sure how to make it work without having to make extra function calls. It would be nice if I could just do all the necessary work inside html.
Research in progress.
Option 2. Use Json.
Stringify the object in the parent component
render() => {
const data = {a:1,b:[1,2,3]}; // of course this is a simplified version of the code
return html`<child-component data=${JSON.stringify(data)}></child-component>`
}
then parse the json in the child component.
This just seems a bit inelegant to me though.
But it works.
In this case what you probably want is to pass the object as a property rather than as an attribute. For complex data such as objects, arrays, functions, etc. that's the preferred method.
You can do it with the following syntax:
render() => {
const data = {a:1,b:[1,2,3]};
// note the period (.), that's the token used to identify that you're passing data as a property
return html`<child-component .data=${data}></child-component>`
}
In general, you should probably give Lit's templating guide a read as some of the most common use cases are covered throughout it.
We are trying to build a component with a property variant that should only be set to "primary" or "secondary" (enum). Currently, we are just declaring the attribute as a String, but we were wondering if there is a better way for handling enums? For example, should we validate somehow that the current value is part of the enum? Should we throw an error if not?
I asked this question on Slack and the answers I got lean towards declaring the property as String and use hasChanged() to display a warning in the console if the property value is invalid.
Standard HTML elements accept any string as attribute values and don't throw exceptions, so web components should probably behave the same way.
This all sounds reasonable to me.
If you're using TypeScript I'd recommend just using strings. You can use export type MyEnum = 'primary' | 'secondary' to declare it and then use #property() fooBar: MyEnum to get build time checking. You can use #ts-check to do this in plain JS with #type MyEnum too.
This works well if the enums are for component options or that map to server-side enums that will get validated again.
However, if you want to validate user input into enums or loop through them a lot this is less good. As the JS runs it has no visibility of the type. You need an object dictionary, something like:
const MyEnum = Object.freeze({
primary: 'primary',
secondary: 'secondary'
});
// Enforce type in TS
const value: keyof MyEnum;
// Validate
const validated = MyEnum[input.toLower()];
// Loop
for(const enumVal of Object.keys(MyEnum)) ...
// Or Convert to a different value type
const MyEnum = Object.freeze({
primary: 1,
secondary: 2
});
These are somewhat idiosyncratic. Again, if you're using TypeScript it has an enum keyword that compiles to something like this and I'd use that rather than rolling your own. Strings are the better option unless you need to validate, loop or convert the values.
Let me preface this by saying I am not a javascript developer, so I'm probably missing something very obvious. I'm a data warehouse developer and creating a graphql server that can communicate with our DW got dropped in my lap.
I've been trying to get dataloaders to work on my graphql server by using a single object in the context, containing multiple dataloaders. I'm then trying to call the appropriate dataloader in the resolver. However, I've been unable to get this to work correctly. The consolidated dataloader object only works if I individually reference the dataloaders in the server context.
I'm trying to follow a similar pattern with the loaders as I have with my models, which is each broken out into a separate file, then consolidated for use as a single object via recursion through the file structure.
Example is I have an object called loaders which contains two loaders: countryLoader and marketsectorLoader, each of which is defined in a separate file under the "loaders" directory. In my server context, the following works
import * as loaders from "./loaders"
graphQLServer.use('/graphql', bodyParser.json(),
graphqlExpress({
schema,
context: {
countryLoader: loaders.countryLoader()
I can then call this in my resolver:
StateProvince: {
Country: (parent, args, {countryLoader}) => {
countryLoader.load(parent.Country_fkey) }},
This functions correctly, batching and returning the correct query result, but I'd prefer not to have to declare each specific dataloader from the loaders object as part of the context. However, I've been unable to figure out the syntax to use the loaders object in the context and call the appropriate
individual dataloader in the appropriate resolver.
I've tried several variants of the following example:
https://github.com/relay-tools/react-relay-network-layer/blob/master/examples/dataLoaderPerBatchRequest.js
which seems to be using the type of technique I'm trying to leverage:
//context snippet:
context: {
request: req, // just for example, pass request to context
dataLoaders: initDataLoaders(),
},
However, no luck. I suspect the issue is with my resolver syntax, but I'm not sure, and I haven't been able to find working examples with multiple dataloaders.
If I'm reading your code correctly, importing your loaders using a wildcard import like this:
import * as loaders from './loaders'
results in an object wherein each property is a function that creates an instance of a particular DataLoader. So we just need to iterate through each property. For example:
// Using forEach
const dataLoaders = {}
Object.keys(loaders).forEach(loaderName => {
dataLoaders[loaderName] = loaders[loaderName]()
})
// Or using reduce
const dataLoaders = Object.keys(loaders).reduce((result, loaderName) => {
result[loaderName] = loaders[loaderName]()
return result
}, {})
Using lodash, you can also just do something like:
const dataLoaders = _.mapValues(loaders, loader => loader())
I have written a piece of software that parses and formats the fourth parameter of a graphql resolver function (the info object) to be used elsewhere. I would like to write unit tests for this software. Specifically, I do not want to build the GraphQLResolveInfo object myself, because doing that would be very cumbersome, error-prone and hard to maintain. Instead, I want to write human-readable query strings and convert them to GraphQLResolveInfo objects so I can pass those to my software.
After extensive googling and reading of the graphql-js source code, I have not found a simple way to do what they are doing internally. I'm really hoping that I am missing something.
What I am not trying to do is use the graphql-tag library, because that just generates an AST which has a very different format from the GraphQLResolveInfo type.
Has anyone done this before? Help would be much appreciated!
I will keep monitoring this question to see if a better answer comes along, but I've finally managed to solve my particular issue by creating as close an approximation of the GraphQLResolveInfo object as I need for my particular use case.
The GraphQLResolveInfo object is composed of several attributes, two of which are called fieldNodes and fragments. Both are in fact parts of the same AST that graphql-tag generates from a query string. These are the only parts of the GraphQLResolveInfo object that concern the software I wrote, the rest of it is ignored.
So here is what I did:
import gql from 'graphql-tag';
// The converter function
const convertQueryToResolveInfo = (query) => {
const operation = query.definitions
.find(({ kind }) => kind === 'OperationDefinition');
const fragments = query.definitions
.filter(({ kind }) => kind === 'FragmentDefinition')
.reduce((result, current) => ({
...result,
[current.name.value]: current,
}), {});
return {
fieldNodes: operation.selectionSet.selections,
fragments,
};
};
// An example call
const query = gql`
query {
foo {
bar
}
}
`;
const info = convertQueryToResolveInfo(query);
From the AST generated by graphql-tag, I extract and modify the operation and fragment definitions so that they look the way they do within the GraphQLResolveInfo object. This is by no means perfect and may be subject to change in the future depending on how my software evolves, but it is a relatively brief solution for my particular problem.
I have this function:
function proc(unames: Array<string>){}
I try to pass it this:
import _ = require('lodash');
const usernames = _.flattenDeep([unames]).filter(function (item, index, arr) {
return item && arr.indexOf(item) === index;
});
const recipient = 'foobarbaz';
proc(usernames.concat(recipient));
I get this error:
Does anyone know how to mitigate this?
I tried this, and I get an even longer and crazier error:
function proc(unames: Array<string | ReadonlyArray<string>>){}
however, this made the error go away:
function proc(unames: Array<string | ReadonlyArray<any>>){}
not really sure what's going on.
The warning seems to be referring to the use of .concat() rather than proc().
When called on an Array, such as usernames, TypeScript is validating that the arguments given to .concat() are also Arrays.
To resolve the warning, you have a few options:
Since you're using Lodash, its own _.concat() allows for appending individual values, and TypeScript's validation should be aware of that:
const recipient = 'foobarbaz';
proc(_.concat(usernames, recipient));
Define recipient as an Array or wrap it when calling .concat():
const recipient = [ 'foobarbaz' ];
proc(usernames.concat(recipient));
const recipient = 'foobarbaz';
proc(usernames.concat( [recipient] ));
You may also be able to configure TypeScript to validate for a later version of ECMAScript. Between 5.1 and 2015 (6th edition) of the standard, the behavior of the built-in .concat() was changed to support individual values (by detecting spreadable).
For now, TypeScript is validating .concat() for 5.1 or older.