Răsfoiți Sursa

fix(dashboard): Fix & add unit tests for getListQueryFields

Michael Bromley 10 luni în urmă
părinte
comite
b1ed66a620

+ 142 - 0
packages/dashboard/src/framework/document-introspection/get-document-structure.spec.ts

@@ -0,0 +1,142 @@
+import { graphql } from 'gql.tada';
+import { parse } from 'graphql';
+import { describe, it, expect } from 'vitest';
+
+import { getListQueryFields } from './get-document-structure.js';
+
+describe('getListQueryFields', () => {
+    it('should extract fields from a simple paginated list query', () => {
+        const doc = graphql(`
+            query {
+                products {
+                    items {
+                        id
+                        name
+                    }
+                }
+            }
+        `);
+
+        const fields = getListQueryFields(doc);
+        expect(fields).toEqual([
+            {
+                isPaginatedList: false,
+                isScalar: true,
+                list: false,
+                name: 'id',
+                nullable: false,
+                type: 'ID',
+            },
+            {
+                isPaginatedList: false,
+                isScalar: true,
+                list: false,
+                name: 'name',
+                nullable: false,
+                type: 'String',
+            },
+        ]);
+    });
+
+    it('should handle a fragment of the main entity in the query', () => {
+        const doc = graphql(`
+            query {
+                products {
+                    items {
+                        ...ProductFields
+                    }
+                }
+            }
+
+            fragment ProductFields on Product {
+                id
+                name
+            }
+        `);
+
+        const fields = getListQueryFields(doc);
+        expect(fields).toEqual([
+            {
+                isPaginatedList: false,
+                isScalar: true,
+                list: false,
+                name: 'id',
+                nullable: false,
+                type: 'ID',
+            },
+            {
+                isPaginatedList: false,
+                isScalar: true,
+                list: false,
+                name: 'name',
+                nullable: false,
+                type: 'String',
+            },
+        ]);
+    });
+
+    it('should handle a fragment of a nested entity in the query', () => {
+        const doc = graphql(/* graphql*/ `
+            query {
+                products {
+                    items {
+                        id
+                        featuredAsset {
+                            ...Asset
+                        }
+                    }
+                }
+            }
+
+            fragment Asset on Asset {
+                preview
+            }
+        `);
+
+        const fields = getListQueryFields(doc);
+        expect(fields).toEqual([
+            {
+                isPaginatedList: false,
+                isScalar: true,
+                list: false,
+                name: 'id',
+                nullable: false,
+                type: 'ID',
+            },
+            {
+                isPaginatedList: false,
+                isScalar: false,
+                list: false,
+                name: 'featuredAsset',
+                nullable: true,
+                type: 'Asset',
+            },
+        ]);
+    });
+
+    it('should return empty array for non-paginated queries', () => {
+        const doc = graphql(`
+            query {
+                product {
+                    id
+                }
+            }
+        `);
+
+        const fields = getListQueryFields(doc);
+        expect(fields).toEqual([]);
+    });
+
+    it('should handle empty items selection set', () => {
+        const doc = graphql(`
+            query {
+                products {
+                    totalItems
+                }
+            }
+        `);
+
+        const fields = getListQueryFields(doc);
+        expect(fields).toEqual([]);
+    });
+});

+ 8 - 0
packages/dashboard/src/framework/document-introspection/get-document-structure.ts

@@ -49,6 +49,9 @@ export function getListQueryFields(documentNode: DocumentNode): FieldInfo[] {
                 const itemsField = queryField.selectionSet?.selections.find(
                     selection => selection.kind === 'Field' && selection.name.value === 'items',
                 ) as FieldNode;
+                if (!itemsField) {
+                    continue;
+                }
                 const typeName = getPaginatedListType(fieldInfo.name);
                 if (!typeName) {
                     throw new Error(`Could not determine type of items in ${fieldInfo.name}`);
@@ -193,6 +196,11 @@ function collectFields(
                 } else if (subSelection.kind === 'FragmentSpread') {
                     const fragmentName = subSelection.name.value;
                     const fragment = fragments[fragmentName];
+                    // We only want to collect fields from the fragment if it's the same type as
+                    // the field we're collecting from
+                    if (fragment.name.value !== typeName) {
+                        return;
+                    }
                     if (fragment) {
                         fragment.selectionSet.selections.forEach(fragmentSelection => {
                             if (fragmentSelection.kind === 'Field') {

+ 1 - 0
packages/dev-server/package.json

@@ -31,6 +31,7 @@
         "concurrently": "^8.2.2",
         "csv-stringify": "^6.4.6",
         "dayjs": "^1.11.10",
+        "jsdom": "^26.0.0",
         "progress": "^2.0.3"
     }
 }

+ 8 - 2
packages/dev-server/vite.config.mts

@@ -1,7 +1,13 @@
 import { vendureDashboardPlugin } from '@vendure/dashboard/plugin';
+import path from 'path';
 import { pathToFileURL } from 'url';
-import { defineConfig } from 'vite';
+import { defineConfig } from 'vitest/config';
 
 export default defineConfig({
-    plugins: [vendureDashboardPlugin({ vendureConfigPath: pathToFileURL('./dev-config.ts') })],
+    test: {
+        globals: true,
+        environment: 'jsdom',
+        root: path.resolve(__dirname, '../dashboard'),
+    },
+    plugins: [vendureDashboardPlugin({ vendureConfigPath: pathToFileURL('./dev-config.ts') }) as any],
 });