Improves code formatting and test coverage#1
Conversation
Standardizes code style across documentation by enforcing double quotes and consistent spacing in markdown examples. Fixes bug in collision detection logic by removing fallback to row object property, ensuring slugs are retrieved correctly from query results. Expands test suite to achieve comprehensive coverage including: - Collision suffix detection with entity exclusion - Custom module configuration options (transliterator, suffixSeparator) - Static instance lifecycle management - Event emission functionality - Edge cases in truncation logic with various separator configurations
There was a problem hiding this comment.
Pull request overview
This PR standardizes README code-example formatting, fixes a collision-suffix query parsing bug in SluggableService, and significantly expands the Vitest test suite for slug generation, subscriber behavior, mixin utilities, and edge cases.
Changes:
- Fix collision detection by relying on the selected raw alias (
row.slug) rather than falling back to dynamic row properties. - Expand test coverage for slugify truncation/separator edge cases, subscriber lifecycle/update behavior, mixin helpers, event emission, and static instance lifecycle.
- Normalize README markdown example style (double quotes, spacing, table formatting).
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
src/sluggable.service.ts |
Fixes collision parsing in generateUniqueSlug() and applies formatting/line-wrapping improvements. |
test/slugify.spec.ts |
Adds new cases for empty separators and truncation edge behaviors. |
test/sluggable.subscriber.spec.ts |
Adds a comprehensive subscriber integration + direct-call test suite, including event emission and early-return paths. |
test/sluggable.service.spec.ts |
Adds additional service tests for collision suffix logic, module-level options, lifecycle, and event emitting. |
test/sluggable.mixin.spec.ts |
Adds mixin behavior tests (slug field, lookup, regeneration) and exception behavior. |
README.md |
Reformats examples/tables for consistency; also removes the Credits section. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| it("should throw SluggableNotInitializedException when service is not available", () => { | ||
| // Ensure static instance is null | ||
| const instance = SluggableService.getInstance(); | ||
| expect(instance).toBeNull(); | ||
|
|
||
| const post = new MixinPost(); | ||
| expect(() => post.findBySlug("test")).rejects.toThrow(SluggableNotInitializedException); |
There was a problem hiding this comment.
This test uses expect(() => post.findBySlug("test")).rejects..., but .rejects must be used on a Promise, not on a function that returns a Promise. As written, the assertion will fail (it won't actually await the rejection). Make the test async and use await expect(post.findBySlug("test")).rejects.toThrow(...) (or return the expectation) so the rejection is properly observed.
| it("should throw SluggableNotInitializedException when service is not available", () => { | |
| // Ensure static instance is null | |
| const instance = SluggableService.getInstance(); | |
| expect(instance).toBeNull(); | |
| const post = new MixinPost(); | |
| expect(() => post.findBySlug("test")).rejects.toThrow(SluggableNotInitializedException); | |
| it("should throw SluggableNotInitializedException when service is not available", async () => { | |
| // Ensure static instance is null | |
| const instance = SluggableService.getInstance(); | |
| expect(instance).toBeNull(); | |
| const post = new MixinPost(); | |
| await expect(post.findBySlug("test")).rejects.toThrow(SluggableNotInitializedException); |
| @@ -1,5 +1,5 @@ | |||
| import "reflect-metadata"; | |||
| import { describe, it, expect, beforeEach, afterEach } from "vitest"; | |||
| import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; | |||
There was a problem hiding this comment.
vi is imported from vitest here but never used anywhere in the file. This will fail linting under the recommended TypeScript ESLint rules (no-unused-vars) and should be removed (or use it if intended).
| import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; | |
| import { describe, it, expect, beforeEach, afterEach } from "vitest"; |
| Please see [CONTRIBUTING](CONTRIBUTING.md) for details. | ||
|
|
||
| ## Security | ||
|
|
||
| If you discover any security-related issues, please report them via [GitHub Issues](https://github.com/nestbolt/sluggable/issues) with the **security** label instead of using the public issue tracker. | ||
|
|
||
| ## Credits | ||
|
|
||
| - Inspired by [spatie/laravel-sluggable](https://github.com/spatie/laravel-sluggable) | ||
|
|
||
| ## License | ||
|
|
||
| The MIT License (MIT). Please see [License File](LICENSE.md) for more information. |
There was a problem hiding this comment.
The PR description mentions formatting changes in the README, but this change also removes the entire "Credits" section (including the attribution link). If the removal is not intentional, please restore it; if it is intentional, it should be called out explicitly in the PR description because it changes published documentation content and attribution.
| it("should handle entity without sluggable metadata on insert", async () => { | ||
| const repo = dataSource.getRepository(SubPlain); | ||
| const plain = repo.create({ name: "Test" }); | ||
| await repo.save(plain); | ||
|
|
||
| expect(plain.id).toBeDefined(); | ||
| }); |
There was a problem hiding this comment.
This test is inside the beforeUpdate describe block but its name says "on insert". Please either move it under the beforeInsert tests or rename it to reflect what it's actually exercising to avoid confusion when failures occur.
|
|
||
| // slug should remain as "original" since empty sourceText returns early | ||
| const found = await repo.findOneBy({ id: note.id }); | ||
| expect(found).toBeDefined(); |
There was a problem hiding this comment.
The comment says the slug should remain "original" when sourceText is empty, but the assertion only checks that found is defined. This doesn't validate the intended behavior and would still pass if the slug were changed/cleared. Assert on the persisted slug value (e.g., expect(found!.slug).toBe("original")) to make this a meaningful regression test.
| expect(found).toBeDefined(); | |
| expect(found).toBeDefined(); | |
| expect(found!.slug).toBe("original"); |
Standardizes code style across documentation by enforcing double quotes and consistent spacing in markdown examples.
Fixes bug in collision detection logic by removing fallback to row object property, ensuring slugs are retrieved correctly from query results.
Expands test suite to achieve comprehensive coverage including: