|
|
@@ -0,0 +1,724 @@
|
|
|
+import { createTestEnvironment } from '@vendure/testing';
|
|
|
+import gql from 'graphql-tag';
|
|
|
+import path from 'path';
|
|
|
+import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
|
+
|
|
|
+import { initialData } from '../../../e2e-common/e2e-initial-data';
|
|
|
+import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
|
|
|
+
|
|
|
+import { LanguageCode } from './graphql/generated-e2e-admin-types';
|
|
|
+import { CREATE_COLLECTION, CREATE_PRODUCT } from './graphql/shared-definitions';
|
|
|
+
|
|
|
+describe('Slug generation', () => {
|
|
|
+ const { server, adminClient } = createTestEnvironment(testConfig());
|
|
|
+
|
|
|
+ beforeAll(async () => {
|
|
|
+ await server.init({
|
|
|
+ initialData,
|
|
|
+ productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
|
|
|
+ customerCount: 1,
|
|
|
+ });
|
|
|
+ await adminClient.asSuperAdmin();
|
|
|
+ }, TEST_SETUP_TIMEOUT_MS);
|
|
|
+
|
|
|
+ afterAll(async () => {
|
|
|
+ await server.destroy();
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('slugForEntity query', () => {
|
|
|
+ describe('basic slug generation', () => {
|
|
|
+ it('generates a simple slug', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Test Product',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('test-product');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles multiple spaces', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Test Product Name',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('test-product-name');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('converts uppercase to lowercase', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'TEST PRODUCT NAME',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('test-product-name');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('preserves numbers', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Product 123 Version 2',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('product-123-version-2');
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('special characters and unicode', () => {
|
|
|
+ it('removes special characters', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Product!@#$%^&*()_+Name',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('productname');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles special characters with spaces', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Product !@#$ Name',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('product-name');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles diacritical marks (accents)', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Café Français naïve résumé',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('cafe-francais-naive-resume');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles German umlauts', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Über größer schön',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('uber-groer-schon');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles Spanish characters', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Niño Español Añejo',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('nino-espanol-anejo');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles non-Latin scripts (removes them)', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Product 商品 المنتج उत्पाद',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('product');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles emoji (removes them)', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Cool Product 😎 🚀 Amazing',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('cool-product-amazing');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles punctuation and symbols', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Product: The Best! (Version 2.0) - New & Improved',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('product-the-best-version-20-new-improved');
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('edge cases', () => {
|
|
|
+ it('handles leading and trailing spaces', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: ' Test Product ',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('test-product');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles hyphens correctly', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Test--Product---Name',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('test-product-name');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles leading and trailing hyphens', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: '-Test Product-',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('test-product');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles empty string', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: '',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles only special characters', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: '!@#$%^&*()',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles mixed case with numbers and special chars', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: '100% Natural & Organic Product #1',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('100-natural-organic-product-1');
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('uniqueness handling', () => {
|
|
|
+ it('appends number for duplicate slugs', async () => {
|
|
|
+ // First, create a product with slug 'laptop'
|
|
|
+ const createProduct = await adminClient.query(CREATE_PRODUCT, {
|
|
|
+ input: {
|
|
|
+ translations: [
|
|
|
+ {
|
|
|
+ languageCode: 'en',
|
|
|
+ name: 'Laptop',
|
|
|
+ slug: 'laptop',
|
|
|
+ description: 'A laptop computer',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ // Now try to generate slug for another product with the same base slug
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Laptop',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('laptop-1');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('increments counter for multiple duplicates', async () => {
|
|
|
+ // Create products with slugs 'phone' and 'phone-1'
|
|
|
+ await adminClient.query(CREATE_PRODUCT, {
|
|
|
+ input: {
|
|
|
+ translations: [
|
|
|
+ {
|
|
|
+ languageCode: 'en',
|
|
|
+ name: 'Phone',
|
|
|
+ slug: 'phone',
|
|
|
+ description: 'A smartphone',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ await adminClient.query(CREATE_PRODUCT, {
|
|
|
+ input: {
|
|
|
+ translations: [
|
|
|
+ {
|
|
|
+ languageCode: 'en',
|
|
|
+ name: 'Phone 2',
|
|
|
+ slug: 'phone-1',
|
|
|
+ description: 'Another smartphone',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ // Now generate slug for another phone
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Phone',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('phone-2');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('excludes own ID when checking uniqueness', async () => {
|
|
|
+ // Create a product
|
|
|
+ const createResult = await adminClient.query(CREATE_PRODUCT, {
|
|
|
+ input: {
|
|
|
+ translations: [
|
|
|
+ {
|
|
|
+ languageCode: 'en',
|
|
|
+ name: 'Tablet',
|
|
|
+ slug: 'tablet',
|
|
|
+ description: 'A tablet device',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ const productId = createResult.createProduct.id;
|
|
|
+
|
|
|
+ // Generate slug for the same product (updating scenario)
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Tablet',
|
|
|
+ entityId: productId,
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ // Should return the same slug without appending number
|
|
|
+ expect(result.slugForEntity).toBe('tablet');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('works with different entity types', async () => {
|
|
|
+ // Test with Collection entity (slug field is in CollectionTranslation)
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Collection',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Summer Collection 2024',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('summer-collection-2024');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles multi-language slug generation', async () => {
|
|
|
+ // Create a product with English translation first
|
|
|
+ const createProduct = await adminClient.query(CREATE_PRODUCT, {
|
|
|
+ input: {
|
|
|
+ translations: [
|
|
|
+ {
|
|
|
+ languageCode: LanguageCode.en,
|
|
|
+ name: 'English Product',
|
|
|
+ slug: 'english-product',
|
|
|
+ description: 'Product in English',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ const productId = createProduct.createProduct.id;
|
|
|
+
|
|
|
+ // Test generating slug for German translation of the same product
|
|
|
+ const germanResult = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Deutsches Produkt',
|
|
|
+ entityId: productId,
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(germanResult.slugForEntity).toBe('deutsches-produkt');
|
|
|
+
|
|
|
+ // Test generating slug for French translation
|
|
|
+ const frenchResult = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Produit Français',
|
|
|
+ entityId: productId,
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(frenchResult.slugForEntity).toBe('produit-francais');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles uniqueness across different language translations', async () => {
|
|
|
+ // Create first product with multiple language translations
|
|
|
+ const product1 = await adminClient.query(CREATE_PRODUCT, {
|
|
|
+ input: {
|
|
|
+ translations: [
|
|
|
+ {
|
|
|
+ languageCode: LanguageCode.en,
|
|
|
+ name: 'Computer',
|
|
|
+ slug: 'computer',
|
|
|
+ description: 'A computer',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ languageCode: LanguageCode.de,
|
|
|
+ name: 'Computer',
|
|
|
+ slug: 'computer-de',
|
|
|
+ description: 'Ein Computer',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ // Generate slug for a new product with same English name
|
|
|
+ const englishSlugResult = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Computer',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(englishSlugResult.slugForEntity).toBe('computer-1');
|
|
|
+
|
|
|
+ // Generate slug with German input that also conflicts
|
|
|
+ const germanSlugResult = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Computer DE',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(germanSlugResult.slugForEntity).toBe('computer-de-1');
|
|
|
+
|
|
|
+ // Generate slug with French input that doesn't conflict
|
|
|
+ const frenchSlugResult = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Ordinateur',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(frenchSlugResult.slugForEntity).toBe('ordinateur');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles translation entity exclusion correctly with multiple languages', async () => {
|
|
|
+ // Create a product with multiple language translations
|
|
|
+ const createProduct = await adminClient.query(CREATE_PRODUCT, {
|
|
|
+ input: {
|
|
|
+ translations: [
|
|
|
+ {
|
|
|
+ languageCode: LanguageCode.en,
|
|
|
+ name: 'Multilingual Product',
|
|
|
+ slug: 'multilingual-product',
|
|
|
+ description: 'Product in English',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ languageCode: LanguageCode.de,
|
|
|
+ name: 'Mehrsprachiges Produkt',
|
|
|
+ slug: 'mehrsprachiges-produkt',
|
|
|
+ description: 'Produkt auf Deutsch',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ const productId = createProduct.createProduct.id;
|
|
|
+
|
|
|
+ // Update English translation - should not conflict with itself
|
|
|
+ const englishUpdateResult = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Multilingual Product Updated',
|
|
|
+ entityId: productId,
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(englishUpdateResult.slugForEntity).toBe('multilingual-product-updated');
|
|
|
+
|
|
|
+ // Update German translation - should not conflict with itself
|
|
|
+ const germanUpdateResult = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Mehrsprachiges Produkt Aktualisiert',
|
|
|
+ entityId: productId,
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(germanUpdateResult.slugForEntity).toBe('mehrsprachiges-produkt-aktualisiert');
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('multi-language collections', () => {
|
|
|
+ it('generates unique slugs for collection translations', async () => {
|
|
|
+ // Create a collection with multiple language translations
|
|
|
+ const createCollection = await adminClient.query(CREATE_COLLECTION, {
|
|
|
+ input: {
|
|
|
+ translations: [
|
|
|
+ {
|
|
|
+ languageCode: LanguageCode.en,
|
|
|
+ name: 'Tech Collection',
|
|
|
+ slug: 'tech-collection',
|
|
|
+ description: 'Technology products',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ languageCode: LanguageCode.fr,
|
|
|
+ name: 'Collection Tech',
|
|
|
+ slug: 'collection-tech',
|
|
|
+ description: 'Produits technologiques',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ filters: [],
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ const collectionId = createCollection.createCollection.id;
|
|
|
+
|
|
|
+ // Test generating new slug for Spanish translation
|
|
|
+ const spanishResult = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Collection',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Colección Tecnológica',
|
|
|
+ entityId: collectionId,
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(spanishResult.slugForEntity).toBe('coleccion-tecnologica');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles collection slug conflicts across languages', async () => {
|
|
|
+ // Create collection with English name
|
|
|
+ await adminClient.query(CREATE_COLLECTION, {
|
|
|
+ input: {
|
|
|
+ translations: [
|
|
|
+ {
|
|
|
+ languageCode: LanguageCode.en,
|
|
|
+ name: 'Fashion Collection',
|
|
|
+ slug: 'fashion-collection',
|
|
|
+ description: 'Fashion items',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ filters: [],
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ // Generate slug for another collection with similar name
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Collection',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Fashion Collection',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('fashion-collection-1');
|
|
|
+
|
|
|
+ // Test with international name that transliterates to similar slug
|
|
|
+ const internationalResult = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Collection',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Façhion Collêction',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(internationalResult.slugForEntity).toBe('fachion-collection');
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('international character handling', () => {
|
|
|
+ it('handles various language scripts in slug generation', async () => {
|
|
|
+ // Test different language inputs
|
|
|
+ const testCases = [
|
|
|
+ { input: 'Café Français', expected: 'cafe-francais' },
|
|
|
+ { input: 'Niño Español', expected: 'nino-espanol' },
|
|
|
+ { input: 'Größer Schön', expected: 'groer-schon' },
|
|
|
+ { input: 'Naïve Résumé', expected: 'naive-resume' },
|
|
|
+ { input: 'Crème Brûlée', expected: 'creme-brulee' },
|
|
|
+ { input: 'Piñata Jalapeño', expected: 'pinata-jalapeno' },
|
|
|
+ ];
|
|
|
+
|
|
|
+ for (const testCase of testCases) {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: testCase.input,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ expect(result.slugForEntity).toBe(testCase.expected);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ it('handles mixed language input', async () => {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'English Français Español Deutsch Mix',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('english-francais-espanol-deutsch-mix');
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('auto-detection functionality', () => {
|
|
|
+ it('auto-detects translation entity for slug field', async () => {
|
|
|
+ // Using base entity name, should automatically detect ProductTranslation
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Auto Detection Test',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('auto-detection-test');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('works with explicit translation entity names', async () => {
|
|
|
+ // Still works when explicitly using translation entity name
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'ProductTranslation',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Explicit Translation Test',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('explicit-translation-test');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('works with Collection entity auto-detection', async () => {
|
|
|
+ // Using base entity name, should automatically detect CollectionTranslation
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Collection',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Collection Auto Detection',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(result.slugForEntity).toBe('collection-auto-detection');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('auto-detects translation entities for different languages', async () => {
|
|
|
+ // Test that auto-detection works regardless of the intended language
|
|
|
+ const testCases = [
|
|
|
+ { input: 'Auto Detection English', expected: 'auto-detection-english' },
|
|
|
+ { input: 'Détection Automatique', expected: 'detection-automatique' },
|
|
|
+ { input: 'Detección Automática', expected: 'deteccion-automatica' },
|
|
|
+ { input: 'Automatische Erkennung', expected: 'automatische-erkennung' },
|
|
|
+ ];
|
|
|
+
|
|
|
+ for (const testCase of testCases) {
|
|
|
+ const result = await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: testCase.input,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ expect(result.slugForEntity).toBe(testCase.expected);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('error handling', () => {
|
|
|
+ it('throws error for non-existent entity', async () => {
|
|
|
+ try {
|
|
|
+ await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'NonExistentEntity',
|
|
|
+ fieldName: 'slug',
|
|
|
+ inputValue: 'Test',
|
|
|
+ },
|
|
|
+ });
|
|
|
+ expect.fail('Should have thrown an error');
|
|
|
+ } catch (error: any) {
|
|
|
+ expect(error.message).toContain('error.entity-not-found');
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ it('throws error for non-existent field', async () => {
|
|
|
+ try {
|
|
|
+ await adminClient.query(SLUG_FOR_ENTITY, {
|
|
|
+ input: {
|
|
|
+ entityName: 'Product',
|
|
|
+ fieldName: 'nonExistentField',
|
|
|
+ inputValue: 'Test',
|
|
|
+ },
|
|
|
+ });
|
|
|
+ expect.fail('Should have thrown an error');
|
|
|
+ } catch (error: any) {
|
|
|
+ expect(error.message).toContain('error.entity-has-no-field');
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+const SLUG_FOR_ENTITY = gql`
|
|
|
+ query SlugForEntity($input: SlugForEntityInput!) {
|
|
|
+ slugForEntity(input: $input)
|
|
|
+ }
|
|
|
+`;
|