forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1import { describe, expect, it } from 'vitest'
2import { mountSuspended } from '@nuxt/test-utils/runtime'
3import LicenseDisplay from '~/components/LicenseDisplay.vue'
4
5describe('LicenseDisplay', () => {
6 describe('single license', () => {
7 it('renders a valid SPDX license as a link', async () => {
8 const component = await mountSuspended(LicenseDisplay, {
9 props: { license: 'MIT' },
10 })
11 const link = component.find('a')
12 expect(link.exists()).toBe(true)
13 expect(link.attributes('href')).toBe('https://spdx.org/licenses/MIT.html')
14 expect(link.text()).toBe('MIT')
15 })
16
17 it('renders an invalid license as plain text', async () => {
18 const component = await mountSuspended(LicenseDisplay, {
19 props: { license: 'CustomLicense' },
20 })
21 const link = component.find('a')
22 expect(link.exists()).toBe(false)
23 expect(component.text()).toContain('CustomLicense')
24 })
25
26 it('shows scales icon for valid license', async () => {
27 const component = await mountSuspended(LicenseDisplay, {
28 props: { license: 'MIT' },
29 })
30 const icon = component.find('.i-lucide\\:scale')
31 expect(icon.exists()).toBe(true)
32 })
33
34 it('does not show scales icon for invalid license', async () => {
35 const component = await mountSuspended(LicenseDisplay, {
36 props: { license: 'CustomLicense' },
37 })
38 const icon = component.find('.i-lucide\\:scale')
39 expect(icon.exists()).toBe(false)
40 })
41 })
42
43 describe('compound expressions with OR', () => {
44 it('renders "MIT OR Apache-2.0" with both licenses linked', async () => {
45 const component = await mountSuspended(LicenseDisplay, {
46 props: { license: 'MIT OR Apache-2.0' },
47 })
48 const links = component.findAll('a')
49 expect(links).toHaveLength(2)
50 expect(links[0]?.attributes('href')).toBe('https://spdx.org/licenses/MIT.html')
51 expect(links[1]?.attributes('href')).toBe('https://spdx.org/licenses/Apache-2.0.html')
52 // Operator is rendered lowercase
53 expect(component.text().toLowerCase()).toContain('or')
54 })
55
56 it('renders triple license choice correctly', async () => {
57 const component = await mountSuspended(LicenseDisplay, {
58 props: { license: '(BSD-2-Clause OR MIT OR Apache-2.0)' },
59 })
60 const links = component.findAll('a')
61 expect(links).toHaveLength(3)
62 })
63 })
64
65 describe('compound expressions with AND', () => {
66 it('renders "MIT AND Zlib" with both licenses linked', async () => {
67 const component = await mountSuspended(LicenseDisplay, {
68 props: { license: 'MIT AND Zlib' },
69 })
70 const links = component.findAll('a')
71 expect(links).toHaveLength(2)
72 expect(links[0]?.attributes('href')).toBe('https://spdx.org/licenses/MIT.html')
73 expect(links[1]?.attributes('href')).toBe('https://spdx.org/licenses/Zlib.html')
74 // Operator is rendered lowercase
75 expect(component.text().toLowerCase()).toContain('and')
76 })
77 })
78
79 describe('compound expressions with WITH', () => {
80 it('renders license with exception', async () => {
81 const component = await mountSuspended(LicenseDisplay, {
82 props: { license: 'GPL-2.0-only WITH Classpath-exception-2.0' },
83 })
84 // GPL-2.0-only is a valid license, Classpath-exception-2.0 is an exception (not a license)
85 const links = component.findAll('a')
86 expect(links.length).toBeGreaterThanOrEqual(1)
87 expect(links[0]?.attributes('href')).toBe('https://spdx.org/licenses/GPL-2.0-only.html')
88 // Operator is rendered lowercase
89 expect(component.text().toLowerCase()).toContain('with')
90 })
91 })
92
93 describe('mixed valid and invalid', () => {
94 it('renders valid licenses as links and invalid as text', async () => {
95 const component = await mountSuspended(LicenseDisplay, {
96 props: { license: 'MIT OR CustomLicense' },
97 })
98 const links = component.findAll('a')
99 expect(links).toHaveLength(1)
100 expect(links[0]?.text()).toBe('MIT')
101 expect(component.text()).toContain('CustomLicense')
102 })
103
104 it('shows scales icon when at least one license is valid', async () => {
105 const component = await mountSuspended(LicenseDisplay, {
106 props: { license: 'CustomLicense OR MIT' },
107 })
108 const icon = component.find('.i-lucide\\:scale')
109 expect(icon.exists()).toBe(true)
110 })
111 })
112
113 describe('parentheses', () => {
114 it('strips parentheses from expressions for cleaner display', async () => {
115 const component = await mountSuspended(LicenseDisplay, {
116 props: { license: '(MIT OR Apache-2.0)' },
117 })
118 // Parentheses are stripped for cleaner display
119 expect(component.text()).not.toContain('(')
120 expect(component.text()).not.toContain(')')
121 const links = component.findAll('a')
122 expect(links).toHaveLength(2)
123 })
124 })
125
126 describe('real-world examples', () => {
127 it('handles rc package license: (BSD-2-Clause OR MIT OR Apache-2.0)', async () => {
128 const component = await mountSuspended(LicenseDisplay, {
129 props: { license: '(BSD-2-Clause OR MIT OR Apache-2.0)' },
130 })
131 const links = component.findAll('a')
132 expect(links).toHaveLength(3)
133 expect(links.map(l => l.text())).toEqual(['BSD-2-Clause', 'MIT', 'Apache-2.0'])
134 })
135
136 it('handles jszip package license: (MIT OR GPL-3.0-or-later)', async () => {
137 const component = await mountSuspended(LicenseDisplay, {
138 props: { license: '(MIT OR GPL-3.0-or-later)' },
139 })
140 const links = component.findAll('a')
141 expect(links).toHaveLength(2)
142 })
143
144 it('handles pako package license: (MIT AND Zlib)', async () => {
145 const component = await mountSuspended(LicenseDisplay, {
146 props: { license: '(MIT AND Zlib)' },
147 })
148 const links = component.findAll('a')
149 expect(links).toHaveLength(2)
150 })
151 })
152})