How to check for level in react-testing-library? - react-testing-library

I have a heading <h4>Big offer!</h4> on the page, when I first ran my tests I got:
Expected: "Big offer!"
Received: <h4>Big offer!</h4>
35 | const switchToggle = screen.getByRole('checkbox');
36 | expect(switchToggle.checked).toEqual(true);
> 37 | expect(titleEl).toEqual(title);
Ok, it get's correct file, so I changed my code to actually check heading level and text, but it failed:
Expected: "Big offer!"
Received: undefined
35 | const switchToggle = screen.getByRole('checkbox');
36 | expect(switchToggle.checked).toEqual(true);
> 37 | expect(titleEl.name).toEqual(title);
| ^
38 | expect(titleEl.level).toEqual(4);
I always thought that .name is equal to getByText(). I commented out a line and tried checking for level, and it failed again:
Expected: 4
Received: undefined
36 | expect(switchToggle.checked).toEqual(true);
37 | //expect(titleEl.name).toEqual(title);
> 38 | expect(titleEl.level).toEqual(4);
I don't understand why my test cases failed. Code for test was:
const title = 'Big offer!';
render(<Component title={title}/>);
const titleEl = screen.getByRole('heading');
expect(titleEl).toEqual(title);
expect(titleEl.level).toEqual(4);

It wont work because screen.getByRole (or others querys) returns an HTMLElement (in your case returns a HTMLHeadingElement that inherits HTMLElement). And it doesnt have name or level properties. You can check more here.
To check the text rendered you should use:
expect(titleEl).toHaveTextContent(title);
And for h4 type check you just need to filter it on query:
const titleEl = screen.getByRole('heading', { level: 4 });
The full test:
it('test search input', async () => {
const title = 'Big offer!';
render(<SearchBox title={title} />);
const titleEl = screen.getByRole('heading', { level: 4 });
expect(titleEl).toBeInTheDocument();
expect(titleEl).toHaveTextContent(title);
});

Related

TypeError: Cannot destructure property 'createNode' of 'boundActionCreators' as it is undefined

Can anyone help with following error ?
ERROR #11321 PLUGIN
"gatsby-source-firestore" threw an error while running the sourceNodes lifecycle:
Cannot destructure property 'createNode' of 'boundActionCreators' as it is undefined.
24 | const db = firebase.firestore();
25 |
26 | const { createNode, createNodeField } = boundActionCreators;
^
27 |
28 | const promises = types.map(
29 | async ({ collection, type, populate, map = node => node }) => {
File: node_modules\gatsby-source-firestore\gatsby-node.js:26:11
TypeError: Cannot destructure property 'createNode' of 'boundActionCreators' as it is undefined.
Replacing boundActionCreators by actions in gatsby-node.js present in node modules/gatsby-source-firestore
worked

How can I setup Jest with a transpiler so that I can have automatically mocked DOM and canvas?

I have a small, browser-based game that I'm trying to get Jest up and running with.
My goal is to be able to write tests, and to have them run with Jest, and not to have any extra DOM- or browser API-related error messages.
As the game makes use of DOM and canvas, I need a solution where I can either mock those manually, or have Jest take care of it for me. At a minimum, I'd like to verify that the 'data model' and my logic is sane.
I'm also making use of ES6 modules.
Here's what I've tried so far:
Tried running jest:
Test suite failed to run
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".
Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/en/ecmascript-modules for how to enable it.
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/en/configuration.html
Details:
/home/dingo/code/game-sscce/game.spec.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import { Game } from './game';
^^^^^^
SyntaxError: Cannot use import statement outside a module
I understood here that I can experimentally enable ES module support, or use a transpiler to output ES5 that Jest can recognize and run.
So my options are:
Enable experimental ES module support
Transpile using Babel
Transpile using Parcel
Transpile using Webpack
I decided to try Babel and looked here for instructions: https://jestjs.io/docs/en/getting-started#using-babel
I created a babel.config.js file in the root directory.
After installing babel and creating a config file, here's an SSCCE:
babel.config.js
module.exports = {
presets: [
[
'#babel/preset-env'
]
],
};
game.js
export class Game {
constructor() {
document.getElementById('gameCanvas').width = 600;
}
}
new Game();
game.spec.js
import { Game } from './game';
test('instantiates Game', () => {
expect(new Game()).toBeDefined();
});
index.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<script type="module" src="game.js" defer></script>
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas" />
</div>
</body>
</html>
package.json
{
"name": "game-sscce",
"version": "1.0.0",
"scripts": {
"test": "jest"
},
"devDependencies": {
"#babel/core": "^7.12.13",
"#babel/preset-env": "^7.12.13",
"babel-jest": "^26.6.3",
"jest": "^26.6.3"
}
}
Now when I try running Jest again, I get:
FAIL ./game.spec.js
● Test suite failed to run
TypeError: Cannot set property 'width' of null
1 | export class Game {
2 | constructor() {
> 3 | document.getElementById('gameCanvas').width = 600;
| ^
4 | }
5 | }
6 |
at new Game (game.js:3:5)
at Object.<anonymous> (game.js:7:1)
at Object.<anonymous> (game.spec.js:1:1)
...and now, I'm not sure what to do. If document is not being recognized, then I suspect Jest is not making use of jsdom properly. Am I supposed to configure anything else?
Investigation:
Jest runs with jsdom by default.
document actually exists:
However, since it's mocked, getElementById() just returns null.
In this situation, it's not possible to return an existing canvas defined in the HTML document. Rather, one can create the canvas programmatically:
game.js
export class Game {
constructor() {
const canvas = document.createElement('canvas');
canvas.setAttribute('id', 'gameCanvas');
document.getElementById('gameContainer').append(canvas);
canvas.width = 600;
}
}
new Game();
getElementById() will, however, still return null, so this call must be mocked:
game.spec.js
import { Game } from './game';
test('instantiates Game', () => {
jest.spyOn(document, 'getElementById').mockReturnValue({})
expect(new Game()).toBeDefined();
});
The test still fails to run:
FAIL ./game.spec.js
● Test suite failed to run
TypeError: Cannot read property 'append' of null
3 | const canvas = document.createElement('canvas');
4 | canvas.setAttribute('id', 'gameCanvas');
> 5 | document.getElementById('gameContainer').append(canvas);
| ^
6 |
7 | canvas.width = 600;
8 |
at new Game (game.js:5:5)
at Object.<anonymous> (game.js:16:1)
at Object.<anonymous> (game.spec.js:1:1)
This is because Game is instantiating itself as soon as Jest imports it due to the new Game() call on the last line. Once rid of that:
game.js
export class Game {
constructor() {
const canvas = document.createElement('canvas');
canvas.setAttribute('id', 'gameCanvas');
document.getElementById('gameContainer').append(canvas);
canvas.width = 600;
}
}
We get:
FAIL ./game.spec.js
✕ instantiates Game (7 ms)
● instantiates Game
TypeError: document.getElementById(...).append is not a function
3 | const canvas = document.createElement('canvas');
4 | canvas.setAttribute('id', 'gameCanvas');
> 5 | document.getElementById('gameContainer').append(canvas);
| ^
6 |
7 | canvas.width = 600;
8 |
at new Game (game.js:5:46)
at Object.<anonymous> (game.spec.js:5:10)
One step closer, but the append() call must also be mocked out:
game.spec.js
import { Game } from './game';
test('instantiates Game', () => {
jest.spyOn(document, 'getElementById').mockReturnValue({
append: jest.fn().mockReturnValue({})
});
expect(new Game()).toBeDefined();
});
...and now the test passes:
PASS ./game.spec.js
✓ instantiates Game (9 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
It's interesting that jsdom returns an HTMLCanvasElement when created programmatically and mocked:
However, it can't really be used for anything:
game.js
export class Game {
constructor() {
const canvas = document.createElement('canvas');
canvas.setAttribute('id', 'gameCanvas');
document.getElementById('gameContainer').append(canvas);
canvas.width = 600;
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'rgb(200, 0, 0)';
ctx.fillRect(10, 10, 50, 50);
ctx.fillStyle = 'rgba(0, 0, 200, 0.5)';
ctx.fillRect(30, 30, 50, 50);
}
}
As shown by the failing test:
FAIL ./game.spec.js
✕ instantiates Game (43 ms)
● instantiates Game
TypeError: Cannot set property 'fillStyle' of null
10 | var ctx = canvas.getContext('2d');
11 |
> 12 | ctx.fillStyle = 'rgb(200, 0, 0)';
| ^
13 | ctx.fillRect(10, 10, 50, 50);
14 |
15 | ctx.fillStyle = 'rgba(0, 0, 200, 0.5)';
at new Game (game.js:12:5)
at Object.<anonymous> (game.spec.js:7:10)
console.error
Error: Not implemented: HTMLCanvasElement.prototype.getContext (without installing the canvas npm package)
at module.exports (/home/dingo/code/game-sscce/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)
at HTMLCanvasElementImpl.getContext (/home/dingo/code/game-sscce/node_modules/jsdom/lib/jsdom/living/nodes/HTMLCanvasElement-impl.js:42:5)
at HTMLCanvasElement.getContext (/home/dingo/code/game-sscce/node_modules/jsdom/lib/jsdom/living/generated/HTMLCanvasElement.js:130:58)
at new Game (/home/dingo/code/game-sscce/game.js:10:22)
at Object.<anonymous> (/home/dingo/code/game-sscce/game.spec.js:7:10)
at Object.asyncJestTest (/home/dingo/code/game-sscce/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
at /home/dingo/code/game-sscce/node_modules/jest-jasmine2/build/queueRunner.js:45:12
at new Promise (<anonymous>)
at mapper (/home/dingo/code/game-sscce/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
at /home/dingo/code/game-sscce/node_modules/jest-jasmine2/build/queueRunner.js:75:41 undefined
8 | canvas.width = 600;
9 |
> 10 | var ctx = canvas.getContext('2d');
| ^
11 |
12 | ctx.fillStyle = 'rgb(200, 0, 0)';
13 | ctx.fillRect(10, 10, 50, 50);
To be able to test further, either of the following two conditions must be fulfilled:
canvas has to be installed as a peer dependency of jsdom,
jest-canvas-mock has to be installed.

Jenkins - Having issues with PostBuild Email notifications

Trying to use the following piece of code to trigger email notifications for a multi-branch pipeline job:
1 def emailNotification() {
2 def to = emailextrecipients([[$class: 'CulpritsRecipientProvider'],
3 [$class: 'DevelopersRecipientProvider'],
4 [$class: 'RequesterRecipientProvider']])
5
6 //def to = "firstname.lastname#domain.com"
7 //String currentResult = currentBuild.result
8 String currentResult = manager.build.getResult()
9 echo "CurrentResult1=${currentResult}"
10 echo "CurrentResult2=${manager.build.getResult()}"
11 echo "CurrentResult3=${manager.build.result}"
12 String previousResult = currentBuild.getPreviousBuild().result
13
14 def causes = currentBuild.rawBuild.getCauses()
15 // E.g. 'started by user', 'triggered by scm change'
16 def cause = null
17 if (!causes.isEmpty()) {
18 cause = causes[0].getShortDescription()
19 }
20
21 // Ensure we don't keep a list of causes, or we get
22 // "java.io.NotSerializableException: hudson.model.Cause$UserIdCause"
23 // see http://stackoverflow.com/a/37897833/509706
25 causes = null
26
27 String subject = "${env.JOB_NAME} ${env.BUILD_NUMBER}: ${currentResult}"
28
29 String body = """
30 <p>Triggered by: <b>${cause}</b></p>
31
32 <p>Last build result: <b>${previousResult}</b></p>
33
34
35 <p>Build <b>${env.BUILD_NUMBER}</b> ran on <b>${env.NODE_NAME}</b> and terminated with <b>${currentResult}</b>.
36 </p>
37
38 <p>See: ${env.BUILD_URL}</p>
39
40 """
41
42 String log = currentBuild.rawBuild.getLog(40).join('\n')
43 if (currentBuild != 'SUCCESS') {
44 body = body + """
45 <h2>Last lines of output</h2>
46 <pre>${log}</pre>
47 """
48 }
49
50 if (to != null && !to.isEmpty()) {
51 // Email on any failures, and on first success.
52 if (currentResult != 'SUCCESS' || currentResult != previousResult) {
53 mail to: to, subject: subject, body: body, mimeType: "text/html"
54 }
55 echo 'Sent email notification'
56 }
57 }
Now, the problems that I'm facing:
def to = emailextrecipients... is not working. I found this and this Jenkins Jira issues that this may be the causes, but no workaround. Although it seems weird that if the build is started manually, say by me a user authenticated through Github Oauth, the mail can be sent. If the Github is starting the build through the webhook, I'm getting this in the Jenkins logs:
Not sending mail to user firstname.lastname#domain.com with no
permission to view
The second issue that I'm seeing is with the PostBuild email trigger.
The Pipeline looks like this:
def emailNotification() {
//the one from above
}
try {
stage('Stage1') {
/*
creating multiple nodes based on an array provided
each node will execute:
checkout scm
buildSolution() //custom method defined
*/
parallel <stuff_above>
}
stage('Stage2') {
//do other stuff
parallel <other_stuff_above>
}
} finally {
emailNotification()
}
The echoes from above (rows 9-11) are all showing null
CurrentResult1=null
CurrentResult2=null
CurrentResult3=null
Using currentBuild.currentResult will show me only SUCCESS or FAILED, but not UNSTABLE, in case some of the tests failed.
Any ideas where the problem is?
Build status is null until something sets it or until the job finishes. Are you using any unit test steps that would cause the build to be unstable?
You don't need to use emailextrecipients instead use.
emailext body: body, mimeType: 'text/html', recipientProviders: [
[$class: 'CulpritsRecipientProvider'],
[$class: 'DevelopersRecipientProvider'],
[$class: 'RequesterRecipientProvider']], subject: subject
Not sending mail to user firstname.lastname#domain.com with no
permission to view
Means that either no jenkins user has this email address associated or the user it is associated with does not have permission to the job
Also for causes put that logic inside a different function and add #NonCPS annotation which will stop jenkins trying to serialise state while that function is running, as you currently have it there is a small chance it will still break with that exception, see https://stackoverflow.com/a/38439681/963402

Using TNT4J, activity statistics are not coming properly

I want to log a activity which starts in the scope of a different class and ends in the scope of another class. Using TrackingLogger.getCurrentActivity() throws an exception as there is no activity in memory.Now if I try to store the activity in static HashMap in singleton Class and retrieve it in the other class, the snapshot of activity is showing confusing values. Here is what I am trying to do :
In first Class :
TrackingLogger tracker = TrackingLogger.getInstance(this.getClass()) ;
TrackingActivity activity = tracker.newActivity(); //
activity.start();
CacheUtil.addTrackingActivity("logger",activity); // Store it in static Hashmap in singleton class
In Second Class :
TrackingLogger tracker = TrackingLogger.getInstance(this.getClass());
TrackingActivity activity = CacheUtil.getTrackingActivity("logger");
activity.stop();
tracker.tnt(activity);
this is the snapshot generated :
18:37:39,780 INFO [Nastel TNT4J] {status: 'END' | time: '2014-07-30 13:07:39.778000 UTC' | sev: 'INFO' | type: 'ACTIVITY' | name: 'NOOP' | usec: '72827000' | wait.usec: '21000' | start.time: '2014-07-30 18:36:26.196000 IST' | end.time: '2014-07-30 18:37:39.023000 IST' | pid: '8092' | tid: '90' | id-count: '0' | snap-count: '6' | source: 'APPL=Nastel TNT4J#JVM=8092#FACH13140035#SERVER=FACH13140035#NETADDR=172.25.19.28#DATACENTER=default#GEOADDR=unknown' | track-id: '09d708c4-a6a9-4200-a70f-25c1da838c11'
Snapshot(CPU#Java) {
Count: 4
TotalCpuUsec: 46800.3
TotalCpuUserUsec: 46800.3
}
Snapshot(Thread#Java) {
Count: 69
DaemonCount: 46
StartedCount: 85
PeakCount: 69
BlockedCount: 7
WaitedCount: 0
BlockedUsec: 21000
WaitUsec: 0
}
Snapshot(Memory#Java) {
MaxBytes: 532742144
TotalBytes: 512499712
FreeBytes: 216341712
UsedBytes: 296158000
Usage: 57
}
Snapshot(Copy#GarbageCollector) {
Count: 477
Time: 1940
isValid: true
}
Snapshot(MarkSweepCompact#GarbageCollector) {
Count: 13
Time: 4652
isValid: true
}
Snapshot(Activity#Java) {
TotalCpuUsec: -1778411.4
SlackUsec: 74584411
WallUsec: -1757411.4
BlockedCount: -24
WaitedCount: -1
BlockedUsec: 21000
WaitUsec: 0
OverheadUsec: 24290.215
}}
Am I missing something?? Pls help ..
TrackingLogger.getCurrentActivity() will only return a valid activity if executed within the same thread. The activity handle is stored in thread local. so you most likely calling TrackingLogger.getCurrentActivity() from a different thread and therefore get an exception.
I am not exactly clear where the confusing values are. Can you elaborate?

django cms plugin instance related_set returns empty list

I have the following models
class NewSlide(models.Model):
slider = models.ForeignKey('NewSliderPlugin')
title = models.CharField(max_length=255)
content = models.TextField(max_length=80, null=True)
link = models.CharField(max_length=255)
image = models.ImageField(upload_to='slides', null=True)
visible = models.BooleanField(default=False)
def __unicode__(self): # Python 3: def __str__(self):
return self.title
class NewSliderPlugin(CMSPlugin):
title = models.CharField(max_length=255)
template = models.CharField(max_length=255, choices=(('slider.html','Top Level Slider'), ('slider2.html','Featured Slider')))
The plugin code as below:
class NewSlideInline(admin.StackedInline):
model = NewSlide
extra = 1
class NewCMSSliderPlugin(CMSPluginBase):
model = NewSliderPlugin
name = "NewSlider"
render_template = "slider.html"
inlines = [NewSlideInline]
def render(self, context, instance, placeholder):
self.render_template = instance.template
print instance.title
print instance.newslide_set.all(), 1111111111111111
context.update({
'slider': instance,
'object': instance,
'placeholder': placeholder
})
return context
I have added slides to the plugin and published changes, however 1instance.newslide_set.all()1 returns empty list: [] 1111111111111111
Update:
it creates 2 records, somehow the admin references 49, but render code gives 63
mysql> select * from cmsplugin_newsliderplugin;
+------------------+-----------+-------------+
| cmsplugin_ptr_id | title | template |
+------------------+-----------+-------------+
| 49 | slide | slider.html |
| 63 | slide | slider.html |
+------------------+-----------+-------------+
mysql> select * from slider_newslide;
+----+-----------+-------+---------+------+----------------+---------+
| id | slider_id | title | content | link | image | visible |
+----+-----------+-------+---------+------+----------------+---------+
| 6 | 49 | ttttt | testt | test | slides/287.jpg | 0 |
+----+-----------+-------+---------+------+----------------+---------+
By the way, I have django-reversion installed, not sure if it's because of this app.
OK according to the documentation I need to copy the related items:
class NewSliderPlugin(CMSPlugin):
title = models.CharField(max_length=255)
template = models.CharField(max_length=255, choices=(('slider.html','Top Level Slider'), ('slider2.html','Featured Slider')))
def copy_relations(self, oldinstance):
for slide in oldinstance.newslide_set.all():
# instance.pk = None; instance.pk.save() is the slightly odd but
# standard Django way of copying a saved model instance
slide.pk = None
slide.slider = self
slide.save()