So I've created some unit test cases inside a component which are all working fine and as intended. However I've got those console errors which were saying:
NG0304: 'mat-card' is not a known element
I've tried to fix those console errors by importing all required material modules and the BrowserAnimationsModule. Now there are no console errors anymore, but 4 of my test are failing now with the following error:
TypeError: Cannot read property 'nativeElement' of null
They all have the same structure but various values.
This is my HTML structure:
<div *ngIf="allowed" class="header">
//material components without *ngIf
<div *ngIf="!show" class="hideDiv">Nothing here</div>
<div *ngIf="show" class=showDiv>Even more material components without *ngIf</div>
</div>
Those are the unit tests which are working:
it('should render div only when allowed is true', () => {
component.allowed= true;
fixture.detectChanges();
const debugElement: DebugElement = fixture.debugElement.query(By.css('.header')).nativeElement;
expect(debugElement).toBeDefined();
});
Those are the unit tests which are not working (and getting null as value for some reason):
it('should render div only when show is true', () => {
component.allowed= true;
component.show= true;
fixture.detectChanges();
const debugElement: DebugElement = fixture.debugElement.query(By.css('.showDiv')).nativeElement;
expect(debugElement).toBeDefined();
});
Related
I have a page that uses the getStaticProps function. The function loads a list of objects that each hold three things: the title of a book, the author of a book, and then a list of characters in the book. The page then maps each of these objects to a component that puts them in a pretty pill.
The component takes the title and author and embeds it into its HTML code. However, the one complexity is the component also uses a useEffect() hook to randomly select 1 character within the character list provided as a prop and then displays them as part of the component HTML. Since it is useEffect(), this does not happen at build time. The reason I want it to occur when the user requests the page is that each user should see a different randomly selected character (i.e., although everything else on the page is the same for all users, I want the character to be randomly selected).
My question is, can getStaticProps work here? Does it build out the HTML as best it can and then when the user requests the page, the character list data is already provided? Or because it uses useEffect(), the component will have to re-request the data from the backend? I want to limit backend requests because the data is stored in AirTable and that only allows 5 calls per second.
index.jsx:
const Home = (props) => {
return (
<div className="flex min-h-screen">
<main className="relative mx-5 mt-16">
{props.response.map((bookData) => (
<BookPill
bookTitle={bookData['Book Title']}
bookAuthor={bookData['Book Author']}
bookCharacters={bookData['Characters']}
/>
))}
</main>
</div>
)
}
export default Home
export async function getStaticProps(context) {
try {
const response = await getBookData()
return {
props: {
response,
},
revalidate: 5,
}
} catch (error) {
return {
props: {
err: 'Something went wrong 😕',
},
}
}
}
BookPill.jsx:
const BookPill = ({
bookTitle,
bookAuthor,
bookCharacters,
}: PropsType) => {
const [randomCharacter, setRandomCharacter] = useState('')
useEffect(() => {
const random_character =
bookCharacters[Math.floor(Math.random() * bookCharacters.length)]
setRandomCharacter(random_character)
}, [])
return (
<div className="my-2 grid grid-cols-1">
<div className="px-5 text-center">
<p className="mb-4">{bookTitle}</p>
<p className="text-sm">{bookAuthor}</p>
</div>
<div className="flex items-center justify-center md:col-span-1">
<div className="rounded-3xl">
{randomCharacter}
</div>
</div>
</div>
)
}
export default BookPill
To summarize what was discussed in the comments:
Because you're using getStaticProps with revalidate: 5, the data will only be fetched on the server, at most every 5 seconds. The data fetched inside getStaticProps is then passed to the HTML generated for the page - you can see the props returned from getStaticProps in the __NEXT_DATA__ script in your HTML.
When the page gets rendered on the browser, the useEffect is triggered and will use that data. No additional requests occur on the client-side.
I was writing some code to map random colors to cells in row.
const COLORS = ['blue', 'green', 'orange', 'red', 'purple', 'yellow'];
const createRandomColors = () => {
const randomColors = [];
for (let i = 0; i < COLORS.length; i++) {
const randomColor = COLORS[Math.floor(Math.random() * COLORS.length)];
randomColors.push(randomColor);
}
return randomColors;
}
const App = () => {
const row = useMemo(createRandomColors, []);
console.log(row);
const cells = useMemo(() => row.map((cell, cellIndex) =>
<div key={cellIndex} style={{ backgroundColor: cell }}>{cellIndex}</div>
), [row]);
cells.forEach(cell => console.log(cell.props.style.backgroundColor));
return (
<div className="app">
<div className="row">
{cells}
</div>
</div>
);
};
export default App;
The problem is that after rendering div elements they have completely different inline style background-color that was specified when mapping divs.
Please see CodeSandBox and take a look at console log and real results rendered.
Why is this happening?
The reason this appears wonky is related to using <StrictMode> in your index.html file. If you remove <StrictMode> you'll see your code works the way you expect. But don't do that, you really do have a bug.
React wants functional components to be idempotent, meaning they do not have side effects. To help you catch side-effects, React will call your code twice back to back when it renders. see strict mode By doing this, it helps uncover subtle issues like the one you're currently experiencing.
One solution is to create the random colors once using useEffect(). Another is to generate the colors outside the functional component.
UPDATE
Please mark the answer as 'accepted' if it solves your issue. You are correct. useMemo will save the computation so it will not be re-computed unless dependencies change. However, react is purposely calling your code twice (in debug mode only) to help you catch unintentional side effects in your classes or hooks. When using strict mode, it's as if you have two of the component instead of one. i.e.
/* StrictMode in debug */
<StrictMode>
<App/>
</StrictMode>
/* ... be like this: */
<>
<App/>
<App/>
</>
If you (temporarily) remove the <StrictMode> tag you'll see your code works as expected. And if you add code that causes your component to render again (e.g. a click counter) your useMemo should prevent the cells from being regenerated each render.
Add a console log to print every time createRandomColors() is called. Since your code is being called twice, you should see the debug log appear twice, but you don't. Why not? React surpasses the console.log the 2nd time it calls your code.
At the top of your code (line 3) add const log = console.log, then replace everywhere you use console.log with just log and you'll have the full picture of what's occurring.
Keep experimenting. We've all been here.
Is there any way in which I could test say the height css property of an element?
I have a function that changes the height of a div and it would be nice to check in the tests that the function operates correctly...
ie:
<div #myDiv>Hello world</div>
#ViewChild('myDiv') myDiv: ElementRef;
myFunct() {
this.myDiv.nativeElement.style.height = 500;
}
it('should increase div size', () => {
// Here is where I get stuck...
});
Update
Implementing a test such as:
it('should return correct height of dropdown when initialised', () => {
component.myFunct();
expect(component.dropdown.nativeElement.style.height).toBe(34);
});
results in a test failure with message:
Expected '' to be 500.
Something like this...
Exposed the myDiv publicly
Setup the testbed for the component (usually added by default)
Add
component.myFunct();
expect(component.myDiv.nativeElement.style.height).toBe(500);
I am trying to insert data into the test fixture but couldn't achieve so far. Returns this error:
"was given a model to stamp, but the template is not of a bindable type"
My test code is like below:
<test-fixture id="myFixture">
<template is="dom-template">
<my-element given-input="[[selectedInput]]"></myElement>
</template>
</test-fixture>
<script>
suite('<my-element>', function() {
var myEl;
setup(function() {
myEl = fixture('myFixture', {selectedInput: 'test input'});
});
test('initiates my-element', function() {
// fails as givenInput returns "[[selectedInput]]"
assert.equal(myEl.givenInput, 'test input');
});
});
</script>
Similar question was asked here polymer 1.0 unit tests - how to bind properties to a child element? but the answer is not what I look for since it is directly defining target property in the child element
Also in Data binding in Polmyer's <test-fixture> it is very same issue but didn't work for me either.
My question is about, how can we pass a property down to the element through test fixture in Polymer 2.x unit testing?
After some more research, I found out that this was an already known issued which can be tracked here https://github.com/PolymerElements/test-fixture/issues/47.
The only possible workaround I have found to continue with the unit testing is to pass givenInput into myEl and removing given-input="[[selectedInput]]" from my-elemet inside test-fixture. Which is not the proper approach but at least makes testing possible.
I'm new to angularjs trying to test HTML elements but my test gets failed.Actually,i want to test the HTML element's value or text which it contain.
can anyone suggest me how can i achieve it?
here is my controllerspec:
describe('myAppCtrl', function() {
var scope, controller, httpBackend;
beforeEach(module('myApp'));
beforeEach(inject(function($rootScope, $controller, $httpBackend) {
scope = $rootScope;
controller = $controller;
httpBackend = $httpBackend;
}));
it('should show the text of element',function() {
expect($('#testtext')).toBe('First Angular JS App');
});
});
What you're doing above is with Karma for unit testing, and is only loading up the modules you've specified. In this case it loads the module myApp, but myApp doesn't have any reference to HTML and doesn't load the DOM itself.
If you want to test HTML elements directly, take a look at end to end testing using Protractor:
https://docs.angularjs.org/guide/e2e-testing