Skip to content

Commit 65d7393

Browse files
authored
JsonView tests (#175)
1 parent 0f096f3 commit 65d7393

File tree

4 files changed

+121
-2
lines changed

4 files changed

+121
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![npm](https://img.shields.io/npm/v/hyperparam)](https://www.npmjs.com/package/hyperparam)
44
[![workflow status](https://github.com/hyparam/hyperparam-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/hyparam/hyperparam-cli/actions)
55
[![mit license](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6-
![coverage](https://img.shields.io/badge/Coverage-59-darkred)
6+
![coverage](https://img.shields.io/badge/Coverage-62-darkred)
77

88
This is the hyperparam cli tool.
99

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,14 @@
5454
"react-dom": "18.3.1"
5555
},
5656
"devDependencies": {
57+
"@eslint/js": "9.22.0",
5758
"@testing-library/react": "16.2.0",
5859
"@types/node": "22.13.9",
5960
"@types/react": "19.0.10",
6061
"@types/react-dom": "19.0.4",
6162
"@vitejs/plugin-react": "4.3.4",
6263
"@vitest/coverage-v8": "3.0.8",
63-
"eslint": "9.21.0",
64+
"eslint": "9.22.0",
6465
"eslint-plugin-react": "7.37.4",
6566
"eslint-plugin-react-hooks": "5.2.0",
6667
"eslint-plugin-react-refresh": "0.4.19",

src/styles/app.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ button.close-button:hover {
525525
height: 24px;
526526
margin-right: auto;
527527
outline: none;
528+
padding: 0;
528529
transition: color 0.3s;
529530
}
530531
.slideClose::before {
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { render, waitFor } from '@testing-library/react'
2+
import React from 'react'
3+
import { describe, expect, it, vi } from 'vitest'
4+
import JsonView from '../../../src/components/viewers/JsonView'
5+
import { FileSource } from '../../../src/lib/sources/types.js'
6+
7+
vi.mock('../../../src/lib/utils.js', async () => {
8+
const actual = await vi.importActual('../../../src/lib/utils.js')
9+
return { ...actual, asyncBufferFrom: vi.fn() }
10+
})
11+
12+
globalThis.fetch = vi.fn()
13+
14+
describe('JsonView Component', () => {
15+
const encoder = new TextEncoder()
16+
17+
it('renders json content as nested lists', async () => {
18+
const body = encoder.encode('{"key":"value"}').buffer as ArrayBuffer
19+
const source: FileSource = {
20+
resolveUrl: 'testKey0',
21+
kind: 'file',
22+
fileName: 'testKey0',
23+
sourceId: 'testKey0',
24+
sourceParts: [],
25+
}
26+
vi.mocked(fetch).mockResolvedValueOnce({
27+
status: 200,
28+
headers: new Headers({ 'Content-Length': body.byteLength.toString() }),
29+
text: () => Promise.resolve('{"key":"value"}'),
30+
} as Response)
31+
32+
const { findByRole, findByText } = render(
33+
<JsonView source={source} setError={console.error} />
34+
)
35+
36+
expect(fetch).toHaveBeenCalledWith('testKey0', undefined)
37+
// Wait for asynchronous JSON loading and parsing
38+
await expect(findByRole('list')).resolves.toBeDefined()
39+
await expect(findByText('key:')).resolves.toBeDefined()
40+
await expect(findByText('"value"')).resolves.toBeDefined()
41+
})
42+
43+
it('displays an error when the json content is too long', async () => {
44+
const source: FileSource = {
45+
sourceId: 'testKey1',
46+
sourceParts: [],
47+
kind: 'file',
48+
fileName: 'testKey1',
49+
resolveUrl: 'testKey1',
50+
}
51+
vi.mocked(fetch).mockResolvedValueOnce({
52+
status: 200,
53+
headers: new Headers({ 'Content-Length': '8000001' }),
54+
text: () => Promise.resolve(''),
55+
} as Response)
56+
57+
const setError = vi.fn()
58+
render(<JsonView source={source} setError={setError} />)
59+
60+
expect(fetch).toHaveBeenCalledWith('testKey1', undefined)
61+
await waitFor(() => {
62+
expect(setError).toHaveBeenCalledWith(expect.objectContaining({
63+
message: 'File is too large to display',
64+
}))
65+
})
66+
})
67+
68+
it('displays an error when the json content is invalid', async () => {
69+
const body = encoder.encode('INVALIDJSON').buffer as ArrayBuffer
70+
const source: FileSource = {
71+
resolveUrl: 'testKey2',
72+
kind: 'file',
73+
fileName: 'testKey2',
74+
sourceId: 'testKey2',
75+
sourceParts: [],
76+
}
77+
vi.mocked(fetch).mockResolvedValueOnce({
78+
status: 200,
79+
headers: new Headers({ 'Content-Length': body.byteLength.toString() }),
80+
text: () => Promise.resolve('INVALIDJSON'),
81+
} as Response)
82+
83+
const setError = vi.fn()
84+
render(<JsonView source={source} setError={setError} />)
85+
86+
expect(fetch).toHaveBeenCalledWith('testKey2', undefined)
87+
await waitFor(() => {
88+
expect(setError).toHaveBeenCalledWith(expect.objectContaining({
89+
message: expect.stringContaining('Unexpected token') as string,
90+
}))
91+
})
92+
})
93+
94+
it('displays an error when unauthorized', async () => {
95+
const source: FileSource = {
96+
resolveUrl: 'testKey3',
97+
kind: 'file',
98+
fileName: 'testKey3',
99+
sourceId: 'testKey3',
100+
sourceParts: [],
101+
}
102+
vi.mocked(fetch).mockResolvedValueOnce({
103+
status: 401,
104+
text: () => Promise.resolve('Unauthorized'),
105+
} as Response)
106+
107+
const setError = vi.fn()
108+
render(<JsonView source={source} setError={setError} />)
109+
110+
expect(fetch).toHaveBeenCalledWith('testKey3', undefined)
111+
await waitFor(() => {
112+
expect(setError).toHaveBeenCalledWith(expect.objectContaining({
113+
message: 'Unauthorized',
114+
}))
115+
})
116+
})
117+
})

0 commit comments

Comments
 (0)