Skip to content

Commit c70af29

Browse files
authored
Merge pull request #300 from jaredwray/chore-upgrading-vitest-to-4.0.3
chore: upgrading vitest to 4.0.3
2 parents b2539e9 + 77e4d1f commit c70af29

File tree

7 files changed

+261
-4
lines changed

7 files changed

+261
-4
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,12 @@
6767
"@types/nunjucks": "^3.2.6",
6868
"@types/pug": "^2.0.10",
6969
"@types/underscore": "^1.13.0",
70-
"@vitest/coverage-v8": "^3.2.4",
70+
"@vitest/coverage-v8": "^4.0.3",
7171
"docula": "^0.30.0",
7272
"rimraf": "^6.0.1",
7373
"tsup": "^8.5.0",
7474
"typescript": "^5.9.2",
75-
"vitest": "^3.2.4",
75+
"vitest": "^4.0.3",
7676
"webpack": "^5.101.3"
7777
},
7878
"files": [

src/ecto.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,9 +298,10 @@ export class Ecto extends Hookified {
298298
await this.writeFile(filePathOutput, result);
299299

300300
return result;
301-
/* c8 ignore next 4 */
302301
} catch (error) {
302+
/* v8 ignore next -- @preserve */
303303
this.emit(EctoEvents.error, error);
304+
/* v8 ignore next -- @preserve */
304305
return "";
305306
}
306307
}
@@ -726,6 +727,7 @@ export class Ecto extends Hookified {
726727
return "handlebars";
727728
}
728729
// Check for basic mustache/handlebars syntax
730+
/* v8 ignore next -- @preserve */
729731
if (source.includes("}}") && !source.includes("{%")) {
730732
return "handlebars";
731733
}
@@ -818,9 +820,9 @@ export class Ecto extends Hookified {
818820
}
819821

820822
// Check for markdown links and images
823+
/* v8 ignore next -- @preserve */
821824
if (
822825
source.includes("](") &&
823-
/* c8 ignore next */
824826
(source.includes("[") || source.includes("!["))
825827
) {
826828
markdownIndicators++;
@@ -834,6 +836,7 @@ export class Ecto extends Hookified {
834836
// Determine if it's Markdown
835837
if (markdownIndicators > 0) {
836838
// Make sure it's not mixed with template syntax
839+
/* v8 ignore next -- @preserve */
837840
if (
838841
!source.includes("<%") &&
839842
!source.includes("{{") &&

test/base-engine.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,11 @@ it("BaseEngine - deleteExtension should be 1 with case", () => {
5656
be.deleteExtension("Md ");
5757
expect(be.getExtensions().length).toBe(1);
5858
});
59+
60+
it("BaseEngine - deleteExtension with non-existent extension should remain same length", () => {
61+
const be = new BaseEngine();
62+
be.setExtensions(["md", "markdown"]);
63+
expect(be.getExtensions().length).toBe(2);
64+
be.deleteExtension("html");
65+
expect(be.getExtensions().length).toBe(2);
66+
});

test/ecto-detect.test.ts

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,4 +501,136 @@ html(lang="en")
501501
expect(ecto.detectEngine(template)).toBe("pug");
502502
});
503503
});
504+
505+
describe("Edge Cases and Additional Coverage", () => {
506+
it("should fallback to default when has <% but incomplete EJS syntax", () => {
507+
// This has <% but not <%=, <%-, or %> - tests the false branch of line 602
508+
// This is an edge case - invalid/incomplete EJS syntax
509+
const template = "Some text with <% only";
510+
// Should fall through to default since it doesn't match complete EJS patterns
511+
expect(ecto.detectEngine(template)).toBe("ejs"); // Falls through to default
512+
});
513+
514+
it("should detect Liquid with filters and {% assign %}", () => {
515+
const template = "{{ name | capitalize }} {% assign foo = 'bar' %}";
516+
expect(ecto.detectEngine(template)).toBe("liquid");
517+
});
518+
519+
it("should detect Liquid with filters and {% capture %}", () => {
520+
const template =
521+
"{{ name | upcase }} {% capture greeting %}Hello{% endcapture %}";
522+
expect(ecto.detectEngine(template)).toBe("liquid");
523+
});
524+
525+
it("should detect Liquid with filters and {% unless %}", () => {
526+
const template =
527+
"{{ price | money }} {% unless sold %}Available{% endunless %}";
528+
expect(ecto.detectEngine(template)).toBe("liquid");
529+
});
530+
531+
it("should detect Handlebars with pipe but not Liquid keywords", () => {
532+
// Has | and }} but not Liquid-specific keywords
533+
const template = "{{ items | length }}";
534+
expect(ecto.detectEngine(template)).toBe("handlebars");
535+
});
536+
537+
it("should detect Handlebars when has }} but not {%", () => {
538+
const template = "Hello {{ name }} and {{ city }}";
539+
expect(ecto.detectEngine(template)).toBe("handlebars");
540+
});
541+
542+
it("should detect Markdown with ordered lists", () => {
543+
const template = `
544+
1. First item
545+
2. Second item
546+
3. Third item
547+
`;
548+
expect(ecto.detectEngine(template)).toBe("markdown");
549+
});
550+
551+
it("should detect Markdown with numbered lists (double digits)", () => {
552+
const template = `
553+
10. Tenth item
554+
11. Eleventh item
555+
`;
556+
expect(ecto.detectEngine(template)).toBe("markdown");
557+
});
558+
559+
it("should detect Markdown with links", () => {
560+
const template = "[Click here](https://example.com)";
561+
expect(ecto.detectEngine(template)).toBe("markdown");
562+
});
563+
564+
it("should detect Markdown with images", () => {
565+
const template = "![Alt text](image.png)";
566+
expect(ecto.detectEngine(template)).toBe("markdown");
567+
});
568+
569+
it("should detect Markdown with headers without template syntax", () => {
570+
const template = `
571+
# Heading 1
572+
## Heading 2
573+
574+
Some content here.
575+
`;
576+
expect(ecto.detectEngine(template)).toBe("markdown");
577+
});
578+
579+
it("should detect Markdown with blockquotes without template syntax", () => {
580+
const template = `
581+
> This is a quote
582+
> Another line of quote
583+
584+
Regular text.
585+
`;
586+
expect(ecto.detectEngine(template)).toBe("markdown");
587+
});
588+
589+
it("should handle edge case with number but not valid ordered list", () => {
590+
// Has a number at start but doesn't form a valid list (no dot and space)
591+
const template = "123 is a number\n456test";
592+
// This should not match markdown ordered list pattern
593+
expect(ecto.detectEngine(template)).toBe("ejs"); // defaults to ejs
594+
});
595+
596+
it("should detect Markdown with unordered lists and avoid template syntax check", () => {
597+
const template = `
598+
# Title
599+
600+
- Item one
601+
- Item two
602+
603+
Some text
604+
`;
605+
expect(ecto.detectEngine(template)).toBe("markdown");
606+
});
607+
608+
it("should not detect Handlebars when has }} AND {%", () => {
609+
// Line 730: has }} but also has {% so condition is false
610+
// This tests the AND condition - it has }} but also {% so it doesn't match handlebars
611+
const template = "{{ name }} {% if test %}";
612+
// Should detect as Nunjucks or Liquid (not Handlebars) since it has {%
613+
expect(ecto.detectEngine(template)).not.toBe("handlebars");
614+
});
615+
616+
it("should handle markdown indicators without ]( link syntax", () => {
617+
// Line 823: has markdown but not the ]( pattern
618+
const template = "# Header\n\nSome text without links";
619+
expect(ecto.detectEngine(template)).toBe("markdown");
620+
});
621+
622+
it("should not detect as markdown when has markdown indicators AND template syntax", () => {
623+
// Line 838: has markdown indicators but also template syntax
624+
const template = "# Header\n\n<%= name %>";
625+
// Should detect as EJS, not markdown
626+
expect(ecto.detectEngine(template)).toBe("ejs");
627+
});
628+
629+
it("should not detect as markdown when has markdown with handlebars syntax", () => {
630+
// Line 838: has markdown indicators but also template syntax
631+
const template = "# Header\n\n{{ name }}";
632+
// Should detect as Handlebars, not markdown
633+
expect(ecto.detectEngine(template)).toBe("handlebars");
634+
});
635+
});
504636
});

test/ecto.test.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,16 @@ it("Find Template without Extension Sync", () => {
426426
expect(filePath).toBe(`${templatePath}/bar.njk`);
427427
});
428428

429+
it("Find Template without Extension Sync - not found", () => {
430+
const ecto = new Ecto();
431+
const templatePath = `${testRootDirectory}/find-templates`;
432+
const filePath = ecto.findTemplateWithoutExtensionSync(
433+
templatePath,
434+
"nonexistent",
435+
);
436+
expect(filePath).toBe("");
437+
});
438+
429439
it("Find Template without Extension on duplicate Sync", async () => {
430440
const ecto = new Ecto();
431441
const templatePath = `${testRootDirectory}/find-templates`;
@@ -448,3 +458,71 @@ it("Render with Configuration via Nunjucks", async () => {
448458

449459
expect(source).toContain("Hello <script>alert('XSS')</script>");
450460
});
461+
462+
it("ensureFilePath - creates directory when it doesn't exist", async () => {
463+
const ecto = new Ecto();
464+
const testPath = `${testOutputDirectory}/new-dir/test.txt`;
465+
466+
// Clean up if it exists
467+
if (fs.existsSync(`${testOutputDirectory}/new-dir`)) {
468+
fs.rmSync(`${testOutputDirectory}/new-dir`, { recursive: true });
469+
}
470+
471+
await ecto.ensureFilePath(testPath);
472+
expect(fs.existsSync(`${testOutputDirectory}/new-dir`)).toBe(true);
473+
474+
// Clean up
475+
fs.rmSync(`${testOutputDirectory}/new-dir`, { recursive: true });
476+
});
477+
478+
it("ensureFilePath - does nothing when directory already exists", async () => {
479+
const ecto = new Ecto();
480+
const testPath = `${testOutputDirectory}/existing-dir/test.txt`;
481+
482+
// Ensure directory exists first
483+
if (!fs.existsSync(`${testOutputDirectory}/existing-dir`)) {
484+
fs.mkdirSync(`${testOutputDirectory}/existing-dir`, { recursive: true });
485+
}
486+
487+
// This should not throw and should handle existing directory
488+
await ecto.ensureFilePath(testPath);
489+
expect(fs.existsSync(`${testOutputDirectory}/existing-dir`)).toBe(true);
490+
491+
// Clean up
492+
fs.rmSync(`${testOutputDirectory}/existing-dir`, { recursive: true });
493+
});
494+
495+
it("ensureFilePathSync - creates directory when it doesn't exist", () => {
496+
const ecto = new Ecto();
497+
const testPath = `${testOutputDirectory}/new-dir-sync/test.txt`;
498+
499+
// Clean up if it exists
500+
if (fs.existsSync(`${testOutputDirectory}/new-dir-sync`)) {
501+
fs.rmSync(`${testOutputDirectory}/new-dir-sync`, { recursive: true });
502+
}
503+
504+
ecto.ensureFilePathSync(testPath);
505+
expect(fs.existsSync(`${testOutputDirectory}/new-dir-sync`)).toBe(true);
506+
507+
// Clean up
508+
fs.rmSync(`${testOutputDirectory}/new-dir-sync`, { recursive: true });
509+
});
510+
511+
it("ensureFilePathSync - does nothing when directory already exists", () => {
512+
const ecto = new Ecto();
513+
const testPath = `${testOutputDirectory}/existing-dir-sync/test.txt`;
514+
515+
// Ensure directory exists first
516+
if (!fs.existsSync(`${testOutputDirectory}/existing-dir-sync`)) {
517+
fs.mkdirSync(`${testOutputDirectory}/existing-dir-sync`, {
518+
recursive: true,
519+
});
520+
}
521+
522+
// This should not throw and should handle existing directory
523+
ecto.ensureFilePathSync(testPath);
524+
expect(fs.existsSync(`${testOutputDirectory}/existing-dir-sync`)).toBe(true);
525+
526+
// Clean up
527+
fs.rmSync(`${testOutputDirectory}/existing-dir-sync`, { recursive: true });
528+
});

test/engine-map.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ it("EngineMap - set with multiple extensions", () => {
1818
expect(mappings.get("ejs")?.length).toBe(3);
1919
});
2020

21+
it("EngineMap - set with duplicate extensions should deduplicate", () => {
22+
const mappings = new EngineMap();
23+
mappings.set("ejs", ["ejs", "md", "ejs", "njk", "md"]);
24+
expect(mappings.get("ejs")?.length).toBe(3);
25+
expect(mappings.get("ejs")?.toString()).toBe("ejs,md,njk");
26+
});
27+
2128
it("EngineMap - set with no extensions should be undefined", () => {
2229
const mappings = new EngineMap();
2330
mappings.set("ejs", []);
@@ -48,6 +55,15 @@ it("EngineMap - deleteExtension with extensions", () => {
4855
expect(mappings.get("ejs")?.toString()).toBe("ejs,md");
4956
});
5057

58+
it("EngineMap - deleteExtension from non-existent engine should not error", () => {
59+
const mappings = new EngineMap();
60+
mappings.set("ejs", ["ejs", "md"]);
61+
// Try to delete extension from an engine that doesn't exist
62+
mappings.deleteExtension("handlebars", "hbs");
63+
// Original engine should still be intact
64+
expect(mappings.get("ejs")?.toString()).toBe("ejs,md");
65+
});
66+
5167
it("EngineMap - getName with extensions", () => {
5268
const mappings = new EngineMap();
5369
mappings.set("ejs", ["ejs", "md", "njk"]);

test/engines/liquid.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,23 @@ it("Liquid - Rendering Partials", async () => {
7373
);
7474
expect(output).toContain("John Doe");
7575
});
76+
77+
it("Liquid - Rendering multiple times with same engine instance (async)", async () => {
78+
const engine = new Liquid();
79+
const firstRender = await engine.render(exampleSource1, exampleData1);
80+
expect(firstRender).toBe("John");
81+
82+
// Second render should reuse the existing engine
83+
const secondRender = await engine.render(exampleSource1, exampleData1);
84+
expect(secondRender).toBe("John");
85+
});
86+
87+
it("Liquid - Rendering multiple times with same engine instance (sync)", () => {
88+
const engine = new Liquid();
89+
const firstRender = engine.renderSync(exampleSource1, exampleData1);
90+
expect(firstRender).toBe("John");
91+
92+
// Second render should reuse the existing engine
93+
const secondRender = engine.renderSync(exampleSource1, exampleData1);
94+
expect(secondRender).toBe("John");
95+
});

0 commit comments

Comments
 (0)