|
1 | 1 | const compiler = require('./compiler.js');
|
2 | 2 |
|
3 |
| -const source = '<style>.red { color: red; }</style>\n<span class="$style.red">Red</span>'; |
4 |
| -const sourceShorthand = '<style>.red { color: red; }</style>\n<span class="$.red">Red</span>'; |
| 3 | +const style = '<style>.red { color: red; }</style>'; |
| 4 | +const source = style + '\n<span class="$style.red">Red</span>'; |
| 5 | +const sourceShorthand = style + '\n<span class="$.red">Red</span>'; |
5 | 6 |
|
6 | 7 | test('Generate CSS Modules from HTML attributes, Replace CSS className', async () => {
|
7 | 8 | const output = await compiler({
|
@@ -55,3 +56,188 @@ test('[Shorthand] Avoid generated class to end with a hyphen', async () => {
|
55 | 56 | });
|
56 | 57 | expect(output).toBe('<style>:global(.red) { color: red; }</style>\n<span class="red">Red</span>');
|
57 | 58 | });
|
| 59 | + |
| 60 | +describe('combining multiple classes', () => { |
| 61 | + const style = '<style>span.red.large:hover { font-size: 20px; } \n.red { color: red; }</style>'; |
| 62 | + const source = style + '\n<span class="$style.red $style.large">Red</span>'; |
| 63 | + |
| 64 | + const expectedStyle = |
| 65 | + '<style>:global(span.red-123456.large-123456:hover) { font-size: 20px; } \n:global(.red-123456) { color: red; }</style>'; |
| 66 | + const expectedOutput = expectedStyle + '\n<span class="red-123456 large-123456">Red</span>'; |
| 67 | + |
| 68 | + test('Generate CSS Modules from HTML attributes, Replace CSS className', async () => { |
| 69 | + const output = await compiler( |
| 70 | + { |
| 71 | + source, |
| 72 | + }, |
| 73 | + { |
| 74 | + localIdentName: '[local]-123456', |
| 75 | + } |
| 76 | + ); |
| 77 | + |
| 78 | + expect(output).toBe(expectedOutput); |
| 79 | + }); |
| 80 | +}); |
| 81 | + |
| 82 | +describe('Classname is part of a selector', () => { |
| 83 | + |
| 84 | + test('CSS Modules class targetting children', async () => { |
| 85 | + const source = |
| 86 | + '<style>\n' + |
| 87 | + 'div.red > sup { font-size: 12px; }\n' + |
| 88 | + '.red { color: red; }\n' + |
| 89 | + '</style>\n' + |
| 90 | + '<div class="$style.red">Red<sup>*</sup></div>'; |
| 91 | + |
| 92 | + const expectedOutput = |
| 93 | + '<style>\n' + |
| 94 | + ':global(div.red-123) > sup { font-size: 12px; }\n' + |
| 95 | + ':global(.red-123) { color: red; }\n' + |
| 96 | + '</style>\n' + |
| 97 | + '<div class="red-123">Red<sup>*</sup></div>'; |
| 98 | + |
| 99 | + const output = await compiler( |
| 100 | + { |
| 101 | + source, |
| 102 | + }, |
| 103 | + { |
| 104 | + localIdentName: '[local]-123', |
| 105 | + } |
| 106 | + ); |
| 107 | + |
| 108 | + expect(output).toBe(expectedOutput); |
| 109 | + }); |
| 110 | + |
| 111 | + test('CSS Modules class has a parent', async () => { |
| 112 | + const source = |
| 113 | + '<style>\n' + |
| 114 | + 'div .semibold .red { font-size: 20px; }\n' + |
| 115 | + '.red { color: red; }\n' + |
| 116 | + '.semibold { font-weight: 600; }\n' + |
| 117 | + '</style>\n' + |
| 118 | + '<div><strong class="$style.semibold"><span class="$style.red">Red</span></strong></div>'; |
| 119 | + |
| 120 | + const expectedOutput = |
| 121 | + '<style>\n' + |
| 122 | + 'div :global(.semibold-123) :global(.red-123) { font-size: 20px; }\n' + |
| 123 | + ':global(.red-123) { color: red; }\n' + |
| 124 | + ':global(.semibold-123) { font-weight: 600; }\n' + |
| 125 | + '</style>\n' + |
| 126 | + '<div><strong class="semibold-123"><span class="red-123">Red</span></strong></div>'; |
| 127 | + |
| 128 | + const output = await compiler( |
| 129 | + { |
| 130 | + source, |
| 131 | + }, |
| 132 | + { |
| 133 | + localIdentName: '[local]-123', |
| 134 | + } |
| 135 | + ); |
| 136 | + |
| 137 | + expect(output).toBe(expectedOutput); |
| 138 | + }); |
| 139 | + |
| 140 | + test('CSS Modules class has a global parent', async () => { |
| 141 | + const source = |
| 142 | + '<style>\n' + |
| 143 | + ':global(div) .red { font-size: 20px; }\n' + |
| 144 | + '.red { color: red; }\n' + |
| 145 | + '</style>\n' + |
| 146 | + '<div><span class="$style.red">Red</span></div>'; |
| 147 | + |
| 148 | + const expectedOutput = |
| 149 | + '<style>\n' + |
| 150 | + ':global(div) :global(.red-123) { font-size: 20px; }\n' + |
| 151 | + ':global(.red-123) { color: red; }\n' + |
| 152 | + '</style>\n' + |
| 153 | + '<div><span class="red-123">Red</span></div>'; |
| 154 | + |
| 155 | + const output = await compiler( |
| 156 | + { |
| 157 | + source, |
| 158 | + }, |
| 159 | + { |
| 160 | + localIdentName: '[local]-123', |
| 161 | + } |
| 162 | + ); |
| 163 | + |
| 164 | + expect(output).toBe(expectedOutput); |
| 165 | + }); |
| 166 | + |
| 167 | + test('CSS Modules class is used within a media query', async () => { |
| 168 | + const source = |
| 169 | + '<style>\n' + |
| 170 | + '@media (min-width: 37.5em) {\n' + |
| 171 | + '.red { color: red; }\n' + |
| 172 | + 'div.bold { font-weight: bold; }\n' + |
| 173 | + '}\n' + |
| 174 | + '</style>\n' + |
| 175 | + '<div class="$style.bold"><span class="$style.red">Red</span></div>'; |
| 176 | + |
| 177 | + const expectedOutput = |
| 178 | + '<style>\n' + |
| 179 | + '@media (min-width: 37.5em) {\n' + |
| 180 | + ':global(.red-123) { color: red; }\n' + |
| 181 | + ':global(div.bold-123) { font-weight: bold; }\n' + |
| 182 | + '}\n' + |
| 183 | + '</style>\n' + |
| 184 | + '<div class="bold-123"><span class="red-123">Red</span></div>'; |
| 185 | + |
| 186 | + const output = await compiler( |
| 187 | + { |
| 188 | + source, |
| 189 | + }, |
| 190 | + { |
| 191 | + localIdentName: '[local]-123', |
| 192 | + } |
| 193 | + ); |
| 194 | + |
| 195 | + expect(output).toBe(expectedOutput); |
| 196 | + }); |
| 197 | +}); |
| 198 | + |
| 199 | +describe('using dynamic classes', () => { |
| 200 | + describe('when matched class is empty', () => { |
| 201 | + // The parser will identify a class named '' |
| 202 | + const source = |
| 203 | + '<style>.red { font-size: 20px; }</style>' + '<span class={`$style.${color}`}>Red</span>'; |
| 204 | + |
| 205 | + test('throws an exception', async () => { |
| 206 | + await expect(compiler({ source })).rejects.toThrow( |
| 207 | + 'Invalid class name in file src/App.svelte.\nThis usually happens when using dynamic classes with svelte-preprocess-cssmodules.' |
| 208 | + ); |
| 209 | + }); |
| 210 | + }); |
| 211 | + |
| 212 | + describe('when matched class could be a valid class but does not match any style definition', () => { |
| 213 | + // The parser will identify a class named 'color' |
| 214 | + const source = |
| 215 | + '<style>.colorred { font-size: 20px; }</style>' + |
| 216 | + '<span class={`$style.color${color}`}>Red</span>'; |
| 217 | + |
| 218 | + it('in strict mode, it throw an exception', async () => { |
| 219 | + await expect(compiler({ source }, { strict: true })).rejects.toThrow( |
| 220 | + 'Classname "color" was not found in declared src/App.svelte <style>' |
| 221 | + ); |
| 222 | + }); |
| 223 | + |
| 224 | + // TODO: fix, this is probably not a result one would expect |
| 225 | + it('in non-strict mode, it removes the resulting class', async () => { |
| 226 | + const output = await compiler({ source }, { strict: false }); |
| 227 | + expect(output).toEqual( |
| 228 | + '<style>.colorred { font-size: 20px; }</style><span class={`${color}`}>Red</span>' |
| 229 | + ); |
| 230 | + }); |
| 231 | + }); |
| 232 | + |
| 233 | + describe('when matched class is an invalid class', () => { |
| 234 | + // The parser will identify a class named 'color-' |
| 235 | + const source = |
| 236 | + '<style>.color-red { font-size: 20px; }</style>' + |
| 237 | + '<span class={`$style.color-${color}`}>Red</span>'; |
| 238 | + |
| 239 | + it('throws an exception when resulting class is invalid', async () => { |
| 240 | + await expect(compiler({ source })).rejects.toThrow('Classname "color-" in file src/App.svelte is not valid'); |
| 241 | + }); |
| 242 | + }); |
| 243 | +}); |
0 commit comments