jestronaut 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/bin/jestronaut.js +48 -0
- package/lib/reporter.js +29 -5
- package/lib/state.js +31 -1
- package/lib/ui/keys.js +1 -0
- package/lib/ui/overlays/suiteDetail.js +1 -1
- package/lib/ui/overlays/testDetail.js +1 -0
- package/lib/ui/panels/footer.js +5 -1
- package/lib/ui/panels/header.js +1 -1
- package/lib/ui/panels/progress.js +3 -4
- package/lib/ui/panels/results.js +10 -10
- package/lib/ui/panels/stats.js +1 -1
- package/lib/ui/panels/suites.js +10 -7
- package/lib/ui/screen.js +34 -8
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -6,6 +6,17 @@ An interactive terminal dashboard for Jest — navigate live test results, suite
|
|
|
6
6
|

|
|
7
7
|

|
|
8
8
|
|
|
9
|
+
## Screenshots
|
|
10
|
+
|
|
11
|
+
### Main Dashboard
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
### Suite Detail
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
### Failure Detail
|
|
18
|
+

|
|
19
|
+
|
|
9
20
|
## Features
|
|
10
21
|
|
|
11
22
|
- Live dashboard updates as tests run
|
package/bin/jestronaut.js
CHANGED
|
@@ -14,6 +14,17 @@ const SUPPRESS = [
|
|
|
14
14
|
'Jest did not exit',
|
|
15
15
|
'detectOpenHandles',
|
|
16
16
|
'asynchronous operations',
|
|
17
|
+
'Watch Usage',
|
|
18
|
+
'Press `',
|
|
19
|
+
'Press a',
|
|
20
|
+
'Press f',
|
|
21
|
+
'Press p',
|
|
22
|
+
'Press t',
|
|
23
|
+
'Press q',
|
|
24
|
+
'Press Enter',
|
|
25
|
+
'No tests found',
|
|
26
|
+
'ran all test suites',
|
|
27
|
+
'Ran all test suites',
|
|
17
28
|
];
|
|
18
29
|
|
|
19
30
|
function shouldSuppress(chunk) {
|
|
@@ -36,6 +47,43 @@ process.stderr.write = (chunk, enc, cb) => {
|
|
|
36
47
|
// Clear terminal before handing off to Jest
|
|
37
48
|
realStdout('\x1b[2J\x1b[H');
|
|
38
49
|
|
|
50
|
+
// Block Jest's watch mode from stealing raw TTY control from blessed.
|
|
51
|
+
// Blessed will call setRawMode itself when the screen is created in the reporter.
|
|
52
|
+
// We block ALL subsequent calls after that so Jest can't interfere.
|
|
53
|
+
const realSetRawMode = process.stdin.setRawMode && process.stdin.setRawMode.bind(process.stdin);
|
|
54
|
+
if (realSetRawMode) {
|
|
55
|
+
let rawModeSet = false;
|
|
56
|
+
process.stdin.setRawMode = (val) => {
|
|
57
|
+
if (!rawModeSet && val) {
|
|
58
|
+
rawModeSet = true;
|
|
59
|
+
return realSetRawMode(val);
|
|
60
|
+
}
|
|
61
|
+
if (!val) {
|
|
62
|
+
// allow turning off (e.g. on exit)
|
|
63
|
+
rawModeSet = false;
|
|
64
|
+
return realSetRawMode(val);
|
|
65
|
+
}
|
|
66
|
+
return process.stdin;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Intercept stdin data events: when TUI is active, only dispatch to blessed's
|
|
71
|
+
// listeners — skip Jest's watch mode listener so it doesn't trigger a re-run.
|
|
72
|
+
const realEmit = process.stdin.emit.bind(process.stdin);
|
|
73
|
+
process.stdin.emit = (event, ...args) => {
|
|
74
|
+
if (event === 'data' && global.__jestronaut_block_jest_input__ && global.__jestronaut_blessed_listeners__) {
|
|
75
|
+
// Call only blessed's listeners, skip Jest's
|
|
76
|
+
global.__jestronaut_blessed_listeners__.forEach(l => l(...args));
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return realEmit(event, ...args);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Global contract for watch mode:
|
|
83
|
+
// __jestronaut_ui__ — { screen, widgets, startTicker, state } created once, reused across re-runs
|
|
84
|
+
// __jestronaut_blessed_listeners__ — Set of stdin 'data' listeners registered by blessed (set in reporter constructor)
|
|
85
|
+
// __jestronaut_block_jest_input__ — boolean; true when TUI should handle all input exclusively (set in renderAll)
|
|
86
|
+
|
|
39
87
|
// Forward all CLI args so flags like --testPathPattern, --watch etc. still work
|
|
40
88
|
const { run } = require('jest');
|
|
41
89
|
run();
|
package/lib/reporter.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { createState } = require('./state');
|
|
3
|
+
const { createState, resetState } = require('./state');
|
|
4
4
|
const { createScreen } = require('./ui/screen');
|
|
5
5
|
const { updateHeader } = require('./ui/panels/header');
|
|
6
6
|
const { updateStats } = require('./ui/panels/stats');
|
|
@@ -10,18 +10,41 @@ const { updateFooter } = require('./ui/panels/footer');
|
|
|
10
10
|
class DashboardReporter {
|
|
11
11
|
constructor(globalConfig) {
|
|
12
12
|
this._globalConfig = globalConfig;
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
const watchMode = globalConfig.watch || globalConfig.watchAll || false;
|
|
14
|
+
// On watch re-runs, reuse the shared state that key handlers reference
|
|
15
|
+
if (global.__jestronaut_ui__) {
|
|
16
|
+
this._state = global.__jestronaut_ui__.state;
|
|
17
|
+
this._state.watchMode = watchMode;
|
|
18
|
+
this._state.watchWaiting = watchMode;
|
|
19
|
+
} else {
|
|
20
|
+
this._state = createState();
|
|
21
|
+
this._state.watchMode = watchMode;
|
|
22
|
+
this._state.watchWaiting = watchMode;
|
|
23
|
+
}
|
|
24
|
+
const beforeScreen = new Set(process.stdin.listeners('data'));
|
|
25
|
+
const { screen, widgets, renderAll, refreshOpenSuiteDetail, startTicker } = createScreen(this._state);
|
|
26
|
+
// Only set blessed listeners once — on first construction when blessed registers them
|
|
27
|
+
if (!global.__jestronaut_blessed_listeners__) {
|
|
28
|
+
global.__jestronaut_blessed_listeners__ = new Set(
|
|
29
|
+
process.stdin.listeners('data').filter(l => !beforeScreen.has(l))
|
|
30
|
+
);
|
|
31
|
+
}
|
|
15
32
|
this._screen = screen;
|
|
16
33
|
this._widgets = widgets;
|
|
17
34
|
this._renderAll = renderAll;
|
|
18
35
|
this._refreshOpenSuiteDetail = refreshOpenSuiteDetail;
|
|
36
|
+
this._startTicker = startTicker;
|
|
19
37
|
}
|
|
20
38
|
|
|
21
39
|
onRunStart(results) {
|
|
22
40
|
const s = this._state;
|
|
41
|
+
resetState(s);
|
|
42
|
+
s.watchWaiting = false;
|
|
43
|
+
this._widgets.suiteDetail.hide();
|
|
44
|
+
this._widgets.testDetail.hide();
|
|
45
|
+
this._startTicker();
|
|
23
46
|
s.stats.suites = results.numTotalTestSuites;
|
|
24
|
-
s.stats.expectedTotal =
|
|
47
|
+
s.stats.expectedTotal = 0;
|
|
25
48
|
s.stats.startTime = Date.now();
|
|
26
49
|
this._renderAll();
|
|
27
50
|
}
|
|
@@ -122,7 +145,8 @@ class DashboardReporter {
|
|
|
122
145
|
`{center}{bold} ${ok ? 'ALL TESTS PASSED' : 'SOME TESTS FAILED'} — ${elapsed}s {/bold}{/center}`
|
|
123
146
|
);
|
|
124
147
|
|
|
125
|
-
s.stats.
|
|
148
|
+
s.stats.endTime = Date.now();
|
|
149
|
+
if (s.watchMode) s.watchWaiting = true;
|
|
126
150
|
updateStats(this._widgets.stats, s.stats);
|
|
127
151
|
updateProgress(this._widgets.progress, s.stats, this._screen.width);
|
|
128
152
|
updateFooter(this._widgets.footer, s);
|
package/lib/state.js
CHANGED
|
@@ -15,6 +15,7 @@ function createState() {
|
|
|
15
15
|
suites: 0,
|
|
16
16
|
suitesCompleted: 0,
|
|
17
17
|
startTime: Date.now(),
|
|
18
|
+
endTime: null,
|
|
18
19
|
},
|
|
19
20
|
|
|
20
21
|
// suite data keyed by file path
|
|
@@ -45,7 +46,36 @@ function createState() {
|
|
|
45
46
|
|
|
46
47
|
// animation
|
|
47
48
|
spinFrame: 0,
|
|
49
|
+
|
|
50
|
+
// watch mode
|
|
51
|
+
watchMode: false,
|
|
52
|
+
watchWaiting: false,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resetState(state) {
|
|
57
|
+
state.stats = {
|
|
58
|
+
passed: 0, failed: 0, skipped: 0,
|
|
59
|
+
total: 0, expectedTotal: 0,
|
|
60
|
+
suites: 0, suitesCompleted: 0,
|
|
61
|
+
startTime: Date.now(),
|
|
62
|
+
endTime: null,
|
|
48
63
|
};
|
|
64
|
+
state.suites = {};
|
|
65
|
+
state.suiteOrder = [];
|
|
66
|
+
state.resultLines = [];
|
|
67
|
+
state.resultMeta = [];
|
|
68
|
+
state.failures = [];
|
|
69
|
+
state.resultCursor = -1;
|
|
70
|
+
state.suiteCursor = 0;
|
|
71
|
+
state.suiteDetailOpen = false;
|
|
72
|
+
state.suiteDetailPath = null;
|
|
73
|
+
state.suiteDetailLines = [];
|
|
74
|
+
state.suiteDetailMeta = [];
|
|
75
|
+
state.suiteDetailCursor = 0;
|
|
76
|
+
state.testDetailOpen = false;
|
|
77
|
+
state.spinFrame = 0;
|
|
78
|
+
// watchMode and watchWaiting are intentionally preserved across resets
|
|
49
79
|
}
|
|
50
80
|
|
|
51
|
-
module.exports = { createState };
|
|
81
|
+
module.exports = { createState, resetState };
|
package/lib/ui/keys.js
CHANGED
|
@@ -119,6 +119,7 @@ function _openSuiteDetail(widgets, state, path) {
|
|
|
119
119
|
widgets.suiteDetail.setLabel(result.label);
|
|
120
120
|
widgets.suiteDetail.style.border.fg = result.hasFailed ? 'red' : 'green';
|
|
121
121
|
widgets.suiteDetail.show();
|
|
122
|
+
widgets.suiteDetail.setFront();
|
|
122
123
|
suiteDetailOverlay.refreshSuiteDetail(widgets.suiteDetail, state);
|
|
123
124
|
}
|
|
124
125
|
|
|
@@ -12,7 +12,7 @@ function createSuiteDetail(screen) {
|
|
|
12
12
|
scrollbar: { ch: '|', style: { fg: 'magenta' } },
|
|
13
13
|
style: {
|
|
14
14
|
border: { fg: 'magenta' }, label: { fg: 'magenta', bold: true },
|
|
15
|
-
bg: '#08080a', item: { fg: 'white' },
|
|
15
|
+
bg: '#08080a', item: { fg: 'white', bg: '#08080a' },
|
|
16
16
|
},
|
|
17
17
|
padding: { left: 2, right: 2, top: 1, bottom: 1 },
|
|
18
18
|
hidden: true,
|
package/lib/ui/panels/footer.js
CHANGED
|
@@ -14,7 +14,8 @@ function createFooter(screen) {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function updateFooter(widget, state) {
|
|
17
|
-
const
|
|
17
|
+
const endTime = state.stats.endTime || Date.now();
|
|
18
|
+
const elapsed = ((endTime - state.stats.startTime) / 1000).toFixed(1);
|
|
18
19
|
const suiteStr = `${state.stats.suitesCompleted}/${state.stats.suites} suites`;
|
|
19
20
|
const spin = SPINNER[state.spinFrame];
|
|
20
21
|
const running = Object.values(state.suites).filter(s => !s.done).length;
|
|
@@ -33,6 +34,9 @@ function _getHint(state) {
|
|
|
33
34
|
if (state.suiteDetailOpen) {
|
|
34
35
|
return '{cyan-fg}[j/k]{/cyan-fg} navigate failed tests | {cyan-fg}[Enter]{/cyan-fg} open failure | {cyan-fg}[Esc]{/cyan-fg} back';
|
|
35
36
|
}
|
|
37
|
+
if (state.watchWaiting) {
|
|
38
|
+
return '{yellow-fg}[a]{/yellow-fg} run all tests';
|
|
39
|
+
}
|
|
36
40
|
if (state.focus === 'results') {
|
|
37
41
|
return '{cyan-fg}[Tab]{/cyan-fg} switch panel | {cyan-fg}[Enter]{/cyan-fg} open failure';
|
|
38
42
|
}
|
package/lib/ui/panels/header.js
CHANGED
|
@@ -4,7 +4,7 @@ const blessed = require('blessed');
|
|
|
4
4
|
|
|
5
5
|
function createHeader(screen) {
|
|
6
6
|
const widget = blessed.box({
|
|
7
|
-
top: 0, left: 0, width: '100%', height:
|
|
7
|
+
top: 0, left: 0, width: '100%', height: 2,
|
|
8
8
|
content: '{center}{bold} JEST TEST DASHBOARD {/bold}{/center}',
|
|
9
9
|
tags: true,
|
|
10
10
|
style: { fg: 'white', bg: 'blue', bold: true },
|
|
@@ -4,7 +4,7 @@ const blessed = require('blessed');
|
|
|
4
4
|
|
|
5
5
|
function createProgress(screen) {
|
|
6
6
|
const widget = blessed.box({
|
|
7
|
-
top:
|
|
7
|
+
top: 4, left: 0, width: '100%', height: 2,
|
|
8
8
|
tags: true,
|
|
9
9
|
style: { fg: 'white', bg: '#111133' },
|
|
10
10
|
});
|
|
@@ -13,9 +13,8 @@ function createProgress(screen) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function updateProgress(widget, stats, screenWidth) {
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const pct = Math.min(100, Math.round((done / total) * 100));
|
|
16
|
+
const suiteTotal = stats.suites || 1;
|
|
17
|
+
const pct = Math.min(100, Math.round((stats.suitesCompleted / suiteTotal) * 100));
|
|
19
18
|
const bar = _buildBar(pct, screenWidth, stats.failed > 0);
|
|
20
19
|
widget.setContent(` Progress: ${bar} ${pct}%`);
|
|
21
20
|
}
|
package/lib/ui/panels/results.js
CHANGED
|
@@ -4,7 +4,7 @@ const blessed = require('blessed');
|
|
|
4
4
|
|
|
5
5
|
function createResults(screen) {
|
|
6
6
|
const widget = blessed.list({
|
|
7
|
-
top:
|
|
7
|
+
top: 6, left: 0, width: '65%', bottom: 3,
|
|
8
8
|
label: ' Test Results ',
|
|
9
9
|
border: { type: 'line' },
|
|
10
10
|
tags: true, scrollable: true, alwaysScroll: true,
|
|
@@ -12,7 +12,8 @@ function createResults(screen) {
|
|
|
12
12
|
scrollbar: { ch: '|', style: { fg: 'cyan' } },
|
|
13
13
|
style: {
|
|
14
14
|
border: { fg: 'cyan' }, label: { fg: 'cyan', bold: true },
|
|
15
|
-
bg: '#0a0a1a', item: { fg: 'white' },
|
|
15
|
+
bg: '#0a0a1a', item: { fg: 'white', bg: '#0a0a1a' },
|
|
16
|
+
selected: { bg: '#1a1a4a', bold: true },
|
|
16
17
|
},
|
|
17
18
|
padding: { left: 1, right: 1 },
|
|
18
19
|
});
|
|
@@ -21,15 +22,14 @@ function createResults(screen) {
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
function refreshResults(widget, state) {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
});
|
|
30
|
-
widget.setItems(items);
|
|
31
|
-
if (state.focus === 'results' && state.resultCursor >= 0) {
|
|
25
|
+
const overlayOpen = state.suiteDetailOpen || state.testDetailOpen;
|
|
26
|
+
const active = !overlayOpen && state.focus === 'results' && state.resultCursor >= 0;
|
|
27
|
+
widget.setItems(state.resultLines);
|
|
28
|
+
if (active) {
|
|
29
|
+
widget.select(state.resultCursor);
|
|
32
30
|
widget.scrollTo(state.resultCursor);
|
|
31
|
+
} else {
|
|
32
|
+
widget.select(-1);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
package/lib/ui/panels/stats.js
CHANGED
package/lib/ui/panels/suites.js
CHANGED
|
@@ -5,7 +5,7 @@ const { SPINNER } = require('../../constants');
|
|
|
5
5
|
|
|
6
6
|
function createSuites(screen) {
|
|
7
7
|
const widget = blessed.list({
|
|
8
|
-
top:
|
|
8
|
+
top: 6, right: 0, width: '35%', bottom: 3,
|
|
9
9
|
label: ' Suites ',
|
|
10
10
|
border: { type: 'line' },
|
|
11
11
|
tags: true, scrollable: true, alwaysScroll: true,
|
|
@@ -13,7 +13,8 @@ function createSuites(screen) {
|
|
|
13
13
|
scrollbar: { ch: '|', style: { fg: 'magenta' } },
|
|
14
14
|
style: {
|
|
15
15
|
border: { fg: 'magenta' }, label: { fg: 'magenta', bold: true },
|
|
16
|
-
bg: '#0a0a1a', item: { fg: 'white' },
|
|
16
|
+
bg: '#0a0a1a', item: { fg: 'white', bg: '#0a0a1a' },
|
|
17
|
+
selected: { bg: '#1a0a3a', bold: true },
|
|
17
18
|
},
|
|
18
19
|
padding: { left: 1, right: 1 },
|
|
19
20
|
});
|
|
@@ -41,17 +42,19 @@ function updateSuites(widget, state) {
|
|
|
41
42
|
? '\n' + [...s.running].map(t => ` {cyan-fg}> ${t}{/cyan-fg}`).join('\n')
|
|
42
43
|
: '';
|
|
43
44
|
|
|
44
|
-
const isCursor = state.focus === 'suites' && i === state.suiteCursor;
|
|
45
|
-
const bg = isCursor ? '{#1a0a3a-bg}' : '';
|
|
46
|
-
const bgEnd = isCursor ? '{/#1a0a3a-bg}' : '';
|
|
45
|
+
const isCursor = state.focus === 'suites' && !state.suiteDetailOpen && !state.testDetailOpen && i === state.suiteCursor;
|
|
47
46
|
const hint = isCursor ? ' {cyan-fg}[Enter]{/cyan-fg}' : '';
|
|
48
47
|
|
|
49
|
-
return `${
|
|
48
|
+
return `${icon} {white-fg}${name}{/white-fg}${detail}${hint}${runningLines}`;
|
|
50
49
|
});
|
|
51
50
|
|
|
52
51
|
widget.setItems(items.length ? items : ['{grey-fg}waiting...{/grey-fg}']);
|
|
53
|
-
|
|
52
|
+
const active = state.focus === 'suites' && !state.suiteDetailOpen && !state.testDetailOpen;
|
|
53
|
+
if (active && state.suiteCursor >= 0) {
|
|
54
|
+
widget.select(state.suiteCursor);
|
|
54
55
|
widget.scrollTo(state.suiteCursor);
|
|
56
|
+
} else {
|
|
57
|
+
widget.select(-1);
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
60
|
|
package/lib/ui/screen.js
CHANGED
|
@@ -13,6 +13,19 @@ const { createTestDetail } = require('./overlays/testDetail');
|
|
|
13
13
|
const { bindKeys, refreshOpenSuiteDetail } = require('./keys');
|
|
14
14
|
|
|
15
15
|
function createScreen(state) {
|
|
16
|
+
// Reuse screen and widgets across watch re-runs — Jest re-instantiates the
|
|
17
|
+
// reporter on every run, but we must not create a second blessed screen.
|
|
18
|
+
if (global.__jestronaut_ui__) {
|
|
19
|
+
const { screen, widgets, startTicker, state: sharedState } = global.__jestronaut_ui__;
|
|
20
|
+
screen.realloc();
|
|
21
|
+
return {
|
|
22
|
+
screen, widgets,
|
|
23
|
+
renderAll: () => renderAll(screen, widgets, sharedState),
|
|
24
|
+
refreshOpenSuiteDetail: () => refreshOpenSuiteDetail(widgets, sharedState),
|
|
25
|
+
startTicker,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
16
29
|
const screen = blessed.screen({
|
|
17
30
|
smartCSR: true,
|
|
18
31
|
title: 'Jest Dashboard',
|
|
@@ -43,20 +56,33 @@ function createScreen(state) {
|
|
|
43
56
|
|
|
44
57
|
bindKeys(screen, widgets, state, render);
|
|
45
58
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
state.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
function startTicker() {
|
|
60
|
+
if (state._ticker) clearInterval(state._ticker);
|
|
61
|
+
state._ticker = setInterval(() => {
|
|
62
|
+
state.spinFrame = (state.spinFrame + 1) % SPINNER.length;
|
|
63
|
+
updateSuites(widgets.suites, state);
|
|
64
|
+
updateFooter(widgets.footer, state);
|
|
65
|
+
screen.render();
|
|
66
|
+
}, 120);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
global.__jestronaut_ui__ = { screen, widgets, startTicker, state };
|
|
53
70
|
|
|
71
|
+
startTicker();
|
|
54
72
|
renderAll(screen, widgets, state);
|
|
55
73
|
|
|
56
|
-
return { screen, widgets, renderAll: () => renderAll(screen, widgets, state), refreshOpenSuiteDetail: () => refreshOpenSuiteDetail(widgets, state) };
|
|
74
|
+
return { screen, widgets, renderAll: () => renderAll(screen, widgets, state), refreshOpenSuiteDetail: () => refreshOpenSuiteDetail(widgets, state), startTicker };
|
|
57
75
|
}
|
|
58
76
|
|
|
59
77
|
function renderAll(screen, widgets, state) {
|
|
78
|
+
// Only pass keypresses to Jest's watch mode when truly idle:
|
|
79
|
+
// watch waiting, no overlays, and no results to navigate
|
|
80
|
+
global.__jestronaut_block_jest_input__ = !(
|
|
81
|
+
state.watchWaiting &&
|
|
82
|
+
!state.suiteDetailOpen &&
|
|
83
|
+
!state.testDetailOpen &&
|
|
84
|
+
state.resultLines.length === 0
|
|
85
|
+
);
|
|
60
86
|
updateStats(widgets.stats, state.stats);
|
|
61
87
|
updateProgress(widgets.progress, state.stats, screen.width);
|
|
62
88
|
updateSuites(widgets.suites, state);
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jestronaut",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "An interactive terminal dashboard UI for Jest — navigate live test results, suites, and failure details without leaving your terminal",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"jestronaut": "bin/jestronaut.js"
|
|
7
|
+
"jestronaut": "./bin/jestronaut.js"
|
|
8
8
|
},
|
|
9
9
|
"keywords": [
|
|
10
10
|
"jest",
|
|
@@ -24,5 +24,14 @@
|
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"jest": ">=27"
|
|
27
|
+
},
|
|
28
|
+
"author": "Deep Nandi",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/realdeepnandi/jestronaut.git"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/realdeepnandi/jestronaut#readme",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/realdeepnandi/jestronaut/issues"
|
|
27
36
|
}
|
|
28
37
|
}
|