meno-core 1.0.45 → 1.0.46

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.
Files changed (56) hide show
  1. package/build-astro.ts +214 -63
  2. package/dist/bin/cli.js +2 -2
  3. package/dist/build-static.js +7 -7
  4. package/dist/chunks/{chunk-NZTSJS5C.js → chunk-2QK6U5UK.js} +3 -2
  5. package/dist/chunks/{chunk-NZTSJS5C.js.map → chunk-2QK6U5UK.js.map} +2 -2
  6. package/dist/chunks/{chunk-BZQKEJQY.js → chunk-77ZB6353.js} +29 -18
  7. package/dist/chunks/chunk-77ZB6353.js.map +7 -0
  8. package/dist/chunks/{chunk-TVH3TC2T.js → chunk-C6U5T5S5.js} +6 -6
  9. package/dist/chunks/{chunk-5ZASE4IG.js → chunk-FED5MME6.js} +234 -11
  10. package/dist/chunks/{chunk-5ZASE4IG.js.map → chunk-FED5MME6.js.map} +3 -3
  11. package/dist/chunks/{chunk-5Z5VQRTJ.js → chunk-I7YIGZXT.js} +4 -4
  12. package/dist/chunks/{chunk-5Z5VQRTJ.js.map → chunk-I7YIGZXT.js.map} +2 -2
  13. package/dist/chunks/{chunk-OUNJ76QM.js → chunk-ORN7S4AP.js} +5 -5
  14. package/dist/chunks/{chunk-GYF3ABI3.js → chunk-UUA5LEWF.js} +3 -3
  15. package/dist/chunks/{chunk-GYF3ABI3.js.map → chunk-UUA5LEWF.js.map} +2 -2
  16. package/dist/chunks/{chunk-WQSG5WHC.js → chunk-ZTKHJQ2Z.js} +2 -2
  17. package/dist/chunks/{chunk-F7MA62WG.js → chunk-ZWYDT3QJ.js} +3 -3
  18. package/dist/chunks/{configService-6KTT6GRT.js → configService-DYCUEURL.js} +3 -3
  19. package/dist/chunks/{constants-L5IKLB6U.js → constants-GWBAD66U.js} +2 -2
  20. package/dist/entries/server-router.js +7 -7
  21. package/dist/lib/client/index.js +4 -4
  22. package/dist/lib/server/index.js +586 -142
  23. package/dist/lib/server/index.js.map +3 -3
  24. package/dist/lib/shared/index.js +7 -3
  25. package/dist/lib/shared/index.js.map +2 -2
  26. package/dist/lib/test-utils/index.js +1 -1
  27. package/lib/client/templateEngine.test.ts +64 -0
  28. package/lib/server/astro/astroEmitHelpers.ts +18 -0
  29. package/lib/server/astro/cmsPageEmitter.ts +31 -1
  30. package/lib/server/astro/componentEmitter.test.ts +59 -0
  31. package/lib/server/astro/componentEmitter.ts +43 -10
  32. package/lib/server/astro/cssCollector.ts +58 -11
  33. package/lib/server/astro/nodeToAstro.test.ts +397 -5
  34. package/lib/server/astro/nodeToAstro.ts +478 -63
  35. package/lib/server/astro/pageEmitter.ts +31 -1
  36. package/lib/server/astro/tailwindMapper.test.ts +119 -0
  37. package/lib/server/astro/tailwindMapper.ts +67 -1
  38. package/lib/server/runtime/httpServer.ts +12 -4
  39. package/lib/server/ssr/htmlGenerator.ts +1 -1
  40. package/lib/server/ssr/jsCollector.ts +2 -2
  41. package/lib/server/ssr/ssrRenderer.test.ts +32 -0
  42. package/lib/server/ssr/ssrRenderer.ts +26 -11
  43. package/lib/shared/constants.ts +1 -0
  44. package/lib/shared/cssGeneration.test.ts +109 -3
  45. package/lib/shared/cssGeneration.ts +98 -13
  46. package/lib/shared/cssNamedColors.ts +47 -0
  47. package/lib/shared/cssProperties.ts +2 -2
  48. package/lib/shared/index.ts +1 -0
  49. package/package.json +1 -1
  50. package/dist/chunks/chunk-BZQKEJQY.js.map +0 -7
  51. /package/dist/chunks/{chunk-TVH3TC2T.js.map → chunk-C6U5T5S5.js.map} +0 -0
  52. /package/dist/chunks/{chunk-OUNJ76QM.js.map → chunk-ORN7S4AP.js.map} +0 -0
  53. /package/dist/chunks/{chunk-WQSG5WHC.js.map → chunk-ZTKHJQ2Z.js.map} +0 -0
  54. /package/dist/chunks/{chunk-F7MA62WG.js.map → chunk-ZWYDT3QJ.js.map} +0 -0
  55. /package/dist/chunks/{configService-6KTT6GRT.js.map → configService-DYCUEURL.js.map} +0 -0
  56. /package/dist/chunks/{constants-L5IKLB6U.js.map → constants-GWBAD66U.js.map} +0 -0
@@ -638,6 +638,95 @@ describe('nodeToAstro', () => {
638
638
  expect(result).toContain('Fragment set:html');
639
639
  expect(result).toContain('<strong>bold</strong>');
640
640
  });
641
+
642
+ test('should prefer processedRawHtml map when raw slice matches', () => {
643
+ const rawSlice = '<p>See <a href="/about">our story</a></p>';
644
+ const processedSlice = '<p>See <a href="/fr/a-propos">our story</a></p>';
645
+ const ctx = createContext({
646
+ processedRawHtml: new Map([[rawSlice, processedSlice]]),
647
+ });
648
+ const result = nodeToAstro(`<!--MENO_RAW_HTML-->${rawSlice}`, ctx);
649
+ expect(result).toContain('Fragment set:html');
650
+ expect(result).toContain('href="/fr/a-propos"');
651
+ expect(result).not.toContain('href="/about"');
652
+ });
653
+
654
+ test('should fall back to raw slice when processedRawHtml is missing the entry', () => {
655
+ const ctx = createContext({
656
+ processedRawHtml: new Map(),
657
+ });
658
+ const result = nodeToAstro('<!--MENO_RAW_HTML--><em>raw</em>', ctx);
659
+ expect(result).toContain('Fragment set:html');
660
+ expect(result).toContain('<em>raw</em>');
661
+ });
662
+
663
+ test('should emit <picture> element from processedRawHtml when image is rewritten', () => {
664
+ const rawSlice = '<img src="foo.png">';
665
+ const processedSlice =
666
+ '<picture><source type="image/avif" srcset="foo.avif" sizes="100vw" /><img src="foo.png" width="100" height="100"></picture>';
667
+ const ctx = createContext({
668
+ processedRawHtml: new Map([[rawSlice, processedSlice]]),
669
+ });
670
+ const result = nodeToAstro(`<!--MENO_RAW_HTML-->${rawSlice}`, ctx);
671
+ expect(result).toContain('<picture>');
672
+ expect(result).toContain('image/avif');
673
+ expect(result).toContain('width="100"');
674
+ });
675
+
676
+ test('should inline expanded component markup from processedRawHtml', () => {
677
+ const rawSlice = '<div data-meno-component="Button" data-meno-props="{}"></div>';
678
+ const processedSlice = '<button class="btn">Click me</button>';
679
+ const ctx = createContext({
680
+ processedRawHtml: new Map([[rawSlice, processedSlice]]),
681
+ });
682
+ const result = nodeToAstro(`<!--MENO_RAW_HTML-->${rawSlice}`, ctx);
683
+ expect(result).toContain('<button class="btn">Click me</button>');
684
+ expect(result).not.toContain('data-meno-component');
685
+ });
686
+
687
+ test('should strip marker from component instance static string props', () => {
688
+ const ctx = createContext();
689
+ const node: ComponentNode = {
690
+ type: 'component',
691
+ component: 'SeoAudit',
692
+ props: { text2: '<!--MENO_RAW_HTML-->SEO<br>Audit Blueprint' },
693
+ } as unknown as ComponentNode;
694
+ const result = nodeToAstro(node, ctx);
695
+ expect(result).not.toContain('MENO_RAW_HTML');
696
+ // Attribute is HTML-entity encoded by escapeJSX; Astro decodes at runtime.
697
+ expect(result).toContain('text2="SEO&lt;br&gt;Audit Blueprint"');
698
+ });
699
+
700
+ test('should strip marker from i18n locale strings in component instance props', () => {
701
+ const ctx = createContext({ isComponentDef: true });
702
+ const node: ComponentNode = {
703
+ type: 'component',
704
+ component: 'SeoAudit',
705
+ props: {
706
+ text2: {
707
+ _i18n: true,
708
+ en: '<!--MENO_RAW_HTML--><p>EN</p>',
709
+ pl: '<!--MENO_RAW_HTML--><p>PL</p>',
710
+ },
711
+ },
712
+ } as unknown as ComponentNode;
713
+ const result = nodeToAstro(node, ctx);
714
+ expect(result).not.toContain('MENO_RAW_HTML');
715
+ expect(result).toContain('<p>EN</p>');
716
+ expect(result).toContain('<p>PL</p>');
717
+ });
718
+
719
+ test('should delegate i18n-resolved string with marker to Fragment set:html', () => {
720
+ const ctx = createContext({ locale: 'en' });
721
+ const node = {
722
+ _i18n: true,
723
+ en: '<!--MENO_RAW_HTML--><em>hello</em>',
724
+ } as unknown as ComponentNode;
725
+ const result = nodeToAstro(node, ctx);
726
+ expect(result).toContain('Fragment set:html');
727
+ expect(result).toContain('<em>hello</em>');
728
+ expect(result).not.toContain('MENO_RAW_HTML');
729
+ });
641
730
  });
642
731
 
643
732
  // =========================================================================
@@ -862,7 +951,7 @@ describe('nodeToAstro', () => {
862
951
  expect(result).toContain('alt="A photo"');
863
952
  });
864
953
 
865
- test('should emit picture element when avif metadata is available', () => {
954
+ test('should emit <Picture> for static image type node with metadata', () => {
866
955
  const ctx = createContext({
867
956
  imageMetadataMap: new Map([
868
957
  ['/images/photo.jpg', {
@@ -879,10 +968,162 @@ describe('nodeToAstro', () => {
879
968
  alt: 'A photo',
880
969
  } as any;
881
970
  const result = nodeToAstro(node, ctx);
882
- expect(result).toContain('<picture');
883
- expect(result).toContain('image/avif');
884
- expect(result).toContain('image/webp');
885
- expect(result).toContain('</picture>');
971
+ expect(result).toContain('<Picture');
972
+ expect(result).toContain('src={imgPhoto}');
973
+ expect(result).toContain("formats={['avif','webp']}");
974
+ expect(result).toContain('widths={[500, 800, 1080, 1600, 2400]}');
975
+ expect(ctx.imageImports?.size ?? 0).toBeGreaterThan(0);
976
+ expect(ctx.imageImports?.get('imgPhoto')).toBe('../assets/images/photo.jpg');
977
+ });
978
+ });
979
+
980
+ // =========================================================================
981
+ // Static image → astro:assets <Picture>
982
+ // =========================================================================
983
+
984
+ describe('static image <Picture> emission', () => {
985
+ const staticMetadata = new Map([
986
+ ['/images/hero.jpg', {
987
+ width: 1920,
988
+ height: 1080,
989
+ srcset: '/images/hero-800.webp 800w',
990
+ avifSrcset: '/images/hero-800.avif 800w',
991
+ }],
992
+ ]);
993
+
994
+ test('static <img> emits <Picture> and records import', () => {
995
+ const ctx = createContext({ imageMetadataMap: staticMetadata as any });
996
+ const node: ComponentNode = {
997
+ type: 'node',
998
+ tag: 'img',
999
+ attributes: { src: '/images/hero.jpg', alt: 'Hero' },
1000
+ } as any;
1001
+ const result = nodeToAstro(node, ctx);
1002
+ expect(result).toContain('<Picture');
1003
+ expect(result).toContain('src={imgHero}');
1004
+ expect(result).toContain('alt="Hero"');
1005
+ expect(ctx.imageImports?.size ?? 0).toBeGreaterThan(0);
1006
+ expect(ctx.imageImports?.get('imgHero')).toBe('../assets/images/hero.jpg');
1007
+ });
1008
+
1009
+ test('page at fileDepth 1 uses ../../ import prefix', () => {
1010
+ const ctx = createContext({
1011
+ imageMetadataMap: staticMetadata as any,
1012
+ fileDepth: 1,
1013
+ });
1014
+ const node: ComponentNode = {
1015
+ type: 'node',
1016
+ tag: 'img',
1017
+ attributes: { src: '/images/hero.jpg', alt: 'Hero' },
1018
+ } as any;
1019
+ nodeToAstro(node, ctx);
1020
+ expect(ctx.imageImports?.get('imgHero')).toBe('../../assets/images/hero.jpg');
1021
+ });
1022
+
1023
+ test('same image used twice dedupes to single import', () => {
1024
+ const ctx = createContext({ imageMetadataMap: staticMetadata as any });
1025
+ const nodes: ComponentNode[] = [
1026
+ { type: 'node', tag: 'img', attributes: { src: '/images/hero.jpg', alt: 'A' } },
1027
+ { type: 'node', tag: 'img', attributes: { src: '/images/hero.jpg', alt: 'B' } },
1028
+ ] as any;
1029
+ nodeToAstro(nodes, ctx);
1030
+ expect(ctx.imageImports?.size).toBe(1);
1031
+ });
1032
+
1033
+ test('CMS template src falls back to legacy <picture>/<img>', () => {
1034
+ const ctx = createContext({
1035
+ imageMetadataMap: staticMetadata as any,
1036
+ cmsMode: true,
1037
+ cmsEntryBinding: 'entry',
1038
+ });
1039
+ const node: ComponentNode = {
1040
+ type: 'node',
1041
+ tag: 'img',
1042
+ attributes: { src: '{{cms.hero}}', alt: 'Hero' },
1043
+ } as any;
1044
+ const result = nodeToAstro(node, ctx);
1045
+ expect(result).not.toContain('<Picture');
1046
+ expect(result).toContain('<img');
1047
+ expect(ctx.imageImports?.size ?? 0).toBe(0);
1048
+ });
1049
+
1050
+ test('list item binding src falls back to legacy <img>', () => {
1051
+ const ctx = createContext({
1052
+ imageMetadataMap: staticMetadata as any,
1053
+ listItemBinding: 'post',
1054
+ });
1055
+ const node: ComponentNode = {
1056
+ type: 'node',
1057
+ tag: 'img',
1058
+ attributes: { src: '{{post.photo}}', alt: 'Photo' },
1059
+ } as any;
1060
+ const result = nodeToAstro(node, ctx);
1061
+ expect(result).not.toContain('<Picture');
1062
+ expect(result).toContain('<img');
1063
+ });
1064
+
1065
+ test('remote URL src falls back to legacy <img>', () => {
1066
+ const ctx = createContext({ imageMetadataMap: staticMetadata as any });
1067
+ const node: ComponentNode = {
1068
+ type: 'node',
1069
+ tag: 'img',
1070
+ attributes: { src: 'https://cdn.example.com/x.jpg', alt: 'Remote' },
1071
+ } as any;
1072
+ const result = nodeToAstro(node, ctx);
1073
+ expect(result).not.toContain('<Picture');
1074
+ expect(result).toContain('<img');
1075
+ });
1076
+
1077
+ test('image not in metadata map falls back to legacy <img>', () => {
1078
+ const ctx = createContext({ imageMetadataMap: staticMetadata as any });
1079
+ const node: ComponentNode = {
1080
+ type: 'node',
1081
+ tag: 'img',
1082
+ attributes: { src: '/images/unknown.jpg', alt: 'Unknown' },
1083
+ } as any;
1084
+ const result = nodeToAstro(node, ctx);
1085
+ expect(result).not.toContain('<Picture');
1086
+ expect(result).toContain('<img');
1087
+ expect(ctx.imageImports?.size ?? 0).toBe(0);
1088
+ });
1089
+
1090
+ test('blur hash puts blur on <picture> via pictureAttributes, no wrapper div', () => {
1091
+ const ctx = createContext({
1092
+ imageMetadataMap: new Map([
1093
+ ['/images/hero.jpg', {
1094
+ width: 1920,
1095
+ height: 1080,
1096
+ srcset: '/images/hero-800.webp 800w',
1097
+ avifSrcset: '/images/hero-800.avif 800w',
1098
+ blurHash: 'data:image/png;base64,blur',
1099
+ }],
1100
+ ]) as any,
1101
+ });
1102
+ const node: ComponentNode = {
1103
+ type: 'node',
1104
+ tag: 'img',
1105
+ attributes: { src: '/images/hero.jpg', alt: 'Hero' },
1106
+ } as any;
1107
+ const result = nodeToAstro(node, ctx);
1108
+ // Blur lives on <picture> via pictureAttributes — no wrapper <div>
1109
+ expect(result).not.toContain('<div');
1110
+ expect(result).toContain('<Picture');
1111
+ expect(result).toContain('pictureAttributes={{');
1112
+ expect(result).toContain('background-image:url');
1113
+ // onload clears the direct parent (<picture>), same as legacy path
1114
+ expect(result).toContain("this.parentElement.style.backgroundImage=''");
1115
+ });
1116
+
1117
+ test('static class attribute maps to pictureAttributes prop', () => {
1118
+ const ctx = createContext({ imageMetadataMap: staticMetadata as any });
1119
+ const node: ComponentNode = {
1120
+ type: 'node',
1121
+ tag: 'img',
1122
+ style: { width: '100%' },
1123
+ attributes: { src: '/images/hero.jpg', alt: 'Hero' },
1124
+ } as any;
1125
+ const result = nodeToAstro(node, ctx);
1126
+ expect(result).toContain('pictureAttributes={{class:');
886
1127
  });
887
1128
  });
888
1129
 
@@ -981,6 +1222,157 @@ describe('nodeToAstro', () => {
981
1222
  expect(result).toContain('srcset=');
982
1223
  expect(result).toContain('sizes=');
983
1224
  });
1225
+
1226
+ test('should compute sizes from flat style width in px', () => {
1227
+ const metadataMap = new Map();
1228
+ metadataMap.set('/test.jpg', {
1229
+ width: 1920,
1230
+ height: 1080,
1231
+ srcset: '/test-800.webp 800w',
1232
+ });
1233
+ const ctx = createContext({ imageMetadataMap: metadataMap as any });
1234
+ const node: ComponentNode = {
1235
+ type: 'node',
1236
+ tag: 'img',
1237
+ attributes: { src: '/test.jpg', alt: 'Test' },
1238
+ style: { width: '444px' },
1239
+ children: [],
1240
+ } as any;
1241
+ const result = nodeToAstro(node, ctx);
1242
+ // With default breakpoints (mobile 540, tablet 1024) and a single base width
1243
+ // of 444px, every breakpoint should resolve to 444px.
1244
+ expect(result).toContain('sizes="(max-width: 540px) 444px, (max-width: 1024px) 444px, 444px"');
1245
+ expect(result).not.toContain('sizes="100vw"');
1246
+ });
1247
+
1248
+ test('should emit per-breakpoint sizes from responsive style widths', () => {
1249
+ const metadataMap = new Map();
1250
+ metadataMap.set('/test.jpg', {
1251
+ width: 1920,
1252
+ height: 1080,
1253
+ srcset: '/test-800.webp 800w',
1254
+ });
1255
+ const ctx = createContext({ imageMetadataMap: metadataMap as any });
1256
+ const node: ComponentNode = {
1257
+ type: 'node',
1258
+ tag: 'img',
1259
+ attributes: { src: '/test.jpg', alt: 'Test' },
1260
+ style: {
1261
+ base: { width: '700px' },
1262
+ tablet: { width: '500px' },
1263
+ mobile: { width: '340px' },
1264
+ },
1265
+ children: [],
1266
+ } as any;
1267
+ const result = nodeToAstro(node, ctx);
1268
+ expect(result).toContain(
1269
+ 'sizes="(max-width: 540px) 340px, (max-width: 1024px) 500px, 700px"'
1270
+ );
1271
+ });
1272
+
1273
+ test('should fall back to 100vw when width is not a px value', () => {
1274
+ const metadataMap = new Map();
1275
+ metadataMap.set('/test.jpg', {
1276
+ width: 1920,
1277
+ height: 1080,
1278
+ srcset: '/test-800.webp 800w',
1279
+ });
1280
+ const ctx = createContext({ imageMetadataMap: metadataMap as any });
1281
+ const node: ComponentNode = {
1282
+ type: 'node',
1283
+ tag: 'img',
1284
+ attributes: { src: '/test.jpg', alt: 'Test' },
1285
+ style: { width: '100%' },
1286
+ children: [],
1287
+ } as any;
1288
+ const result = nodeToAstro(node, ctx);
1289
+ expect(result).toContain('sizes="100vw"');
1290
+ });
1291
+
1292
+ test('should honor explicit sizes attribute from node', () => {
1293
+ const metadataMap = new Map();
1294
+ metadataMap.set('/test.jpg', {
1295
+ width: 1920,
1296
+ height: 1080,
1297
+ srcset: '/test-800.webp 800w',
1298
+ });
1299
+ const ctx = createContext({ imageMetadataMap: metadataMap as any });
1300
+ const node: ComponentNode = {
1301
+ type: 'node',
1302
+ tag: 'img',
1303
+ attributes: { src: '/test.jpg', alt: 'Test', sizes: '50vw' },
1304
+ style: { width: '444px' },
1305
+ children: [],
1306
+ } as any;
1307
+ const result = nodeToAstro(node, ctx);
1308
+ expect(result).toContain('sizes="50vw"');
1309
+ expect(result).not.toContain('(max-width: 540px)');
1310
+ });
1311
+
1312
+ test('should wrap webp img in <picture> with blur background when blurHash present', () => {
1313
+ const metadataMap = new Map();
1314
+ metadataMap.set('/test.jpg', {
1315
+ width: 1920,
1316
+ height: 1080,
1317
+ srcset: '/test-800.webp 800w',
1318
+ blurHash: 'data:image/webp;base64,AAA',
1319
+ });
1320
+ const ctx = createContext({ imageMetadataMap: metadataMap as any });
1321
+ const node: ComponentNode = {
1322
+ type: 'node',
1323
+ tag: 'img',
1324
+ attributes: { src: '/test.jpg', alt: 'Test' },
1325
+ style: { width: '444px' },
1326
+ children: [],
1327
+ } as any;
1328
+ const result = nodeToAstro(node, ctx);
1329
+
1330
+ // Blur should be on the <picture> wrapper, not the <img>
1331
+ expect(result).toContain('<picture');
1332
+ expect(result).toContain('</picture>');
1333
+ expect(result).toContain('background-image:url(data:image/webp;base64,AAA)');
1334
+ // The <picture>'s style must carry the background, and the <img> must not.
1335
+ const pictureLine = result.split('\n').find(l => l.includes('<picture'));
1336
+ expect(pictureLine).toContain('background-image:url');
1337
+ const imgLine = result.split('\n').find(l => l.includes('<img'));
1338
+ expect(imgLine).not.toContain('background-image');
1339
+ // The onload handler should clear the placeholder from the parent.
1340
+ expect(result).toContain("this.parentElement.style.backgroundImage=''");
1341
+ // Inner img must fill the wrapper.
1342
+ expect(imgLine).toContain('class="block w-full h-full"');
1343
+ });
1344
+
1345
+ test('should place blur on <picture> wrapper even when AVIF is present', () => {
1346
+ const metadataMap = new Map();
1347
+ metadataMap.set('/test.jpg', {
1348
+ width: 1920,
1349
+ height: 1080,
1350
+ srcset: '/test-800.webp 800w',
1351
+ avifSrcset: '/test-800.avif 800w',
1352
+ blurHash: 'data:image/webp;base64,BBB',
1353
+ });
1354
+ const ctx = createContext({ imageMetadataMap: metadataMap as any });
1355
+ const node: ComponentNode = {
1356
+ type: 'node',
1357
+ tag: 'img',
1358
+ attributes: { src: '/test.jpg', alt: 'Test' },
1359
+ style: { width: '444px', height: '268px', position: 'absolute' },
1360
+ children: [],
1361
+ } as any;
1362
+ const result = nodeToAstro(node, ctx);
1363
+ expect(result).toContain('<picture');
1364
+ expect(result).toContain('type="image/avif"');
1365
+ expect(result).toContain('type="image/webp"');
1366
+ const pictureLine = result.split('\n').find(l => l.includes('<picture'));
1367
+ expect(pictureLine).toContain('background-image:url(data:image/webp;base64,BBB)');
1368
+ // Inner img should not carry an inline background-image anymore.
1369
+ const imgLine = result.split('\n').find(l => l.includes('<img'));
1370
+ expect(imgLine).not.toContain('background-image');
1371
+ // Fill classes on inner img so it covers the picture's declared size.
1372
+ expect(imgLine).toContain('block');
1373
+ expect(imgLine).toContain('w-full');
1374
+ expect(imgLine).toContain('h-full');
1375
+ });
984
1376
  });
985
1377
 
986
1378
  // =========================================================================