* feat: add view_configurations feature flag - Add feature flag provider and hooks to admin dashboard - Add backend API endpoint for feature flags - Create view_configurations feature flag (disabled by default) - Update order list table to use legacy version when flag is disabled - Can be enabled with MEDUSA_FF_VIEW_CONFIGURATIONS=true env var * fix: naming * fix: feature flags unauthenticated * fix: add test * feat: add settings module * fix: deps * fix: cleanup * fix: add more tetsts * fix: rm changelog * fix: deps * fix: add settings module to default modules list * fix: pr comments * fix: deps,build * fix: alias * fix: tests * fix: update snapshots
424 lines
16 KiB
TypeScript
424 lines
16 KiB
TypeScript
import { Modules } from "@medusajs/utils"
|
|
import { moduleIntegrationTestRunner } from "@medusajs/test-utils"
|
|
import { SettingsTypes } from "@medusajs/types"
|
|
|
|
jest.setTimeout(30000)
|
|
|
|
moduleIntegrationTestRunner<SettingsTypes.ISettingsModuleService>({
|
|
moduleName: Modules.SETTINGS,
|
|
testSuite: ({ service }) => {
|
|
describe("SettingsModuleService", function () {
|
|
describe("ViewConfiguration", function () {
|
|
it("should create a view configuration", async () => {
|
|
const viewConfig = await service.createViewConfigurations({
|
|
entity: "orders",
|
|
name: "My Orders View",
|
|
user_id: "user_123",
|
|
configuration: {
|
|
visible_columns: ["id", "status", "created_at"],
|
|
column_order: ["id", "status", "created_at"],
|
|
column_widths: { id: 100, status: 150 },
|
|
filters: { status: ["pending", "completed"] },
|
|
sorting: { id: "created_at", desc: true },
|
|
search: "",
|
|
},
|
|
})
|
|
|
|
expect(viewConfig).toEqual(
|
|
expect.objectContaining({
|
|
id: expect.any(String),
|
|
entity: "orders",
|
|
name: "My Orders View",
|
|
user_id: "user_123",
|
|
})
|
|
)
|
|
})
|
|
|
|
it("should update a view configuration and remove filters", async () => {
|
|
// Create a view with filters
|
|
const viewConfig = await service.createViewConfigurations({
|
|
entity: "products",
|
|
name: "Filtered Products View",
|
|
user_id: "user_456",
|
|
configuration: {
|
|
visible_columns: ["id", "title", "status"],
|
|
column_order: ["id", "title", "status"],
|
|
filters: {
|
|
status: ["draft", "published"],
|
|
collection_id: ["col_123", "col_456"],
|
|
},
|
|
sorting: { id: "created_at", desc: true },
|
|
},
|
|
})
|
|
|
|
expect(viewConfig.configuration.filters).toEqual({
|
|
status: ["draft", "published"],
|
|
collection_id: ["col_123", "col_456"],
|
|
})
|
|
|
|
// Update the view to remove filters
|
|
const updatedConfig = await service.updateViewConfigurations(
|
|
viewConfig.id,
|
|
{
|
|
configuration: {
|
|
visible_columns: ["id", "title", "status"],
|
|
column_order: ["id", "title", "status"],
|
|
filters: {}, // Empty filters object
|
|
sorting: { id: "created_at", desc: true },
|
|
},
|
|
}
|
|
)
|
|
|
|
expect(updatedConfig.configuration.filters).toEqual({})
|
|
|
|
// Retrieve the view again to ensure filters were persisted as empty
|
|
const retrievedConfig = await service.retrieveViewConfiguration(
|
|
viewConfig.id
|
|
)
|
|
|
|
expect(retrievedConfig.configuration.filters).toEqual({})
|
|
})
|
|
|
|
it("should update view configuration with partial configuration updates", async () => {
|
|
// Create a view with full configuration
|
|
const viewConfig = await service.createViewConfigurations({
|
|
entity: "customers",
|
|
name: "Customer View",
|
|
user_id: "user_789",
|
|
configuration: {
|
|
visible_columns: ["id", "name", "email"],
|
|
column_order: ["id", "name", "email"],
|
|
filters: {
|
|
has_account: true,
|
|
groups: ["vip", "regular"],
|
|
},
|
|
sorting: { id: "created_at", desc: false },
|
|
search: "test search",
|
|
},
|
|
})
|
|
|
|
// Update only filters (should preserve other configuration)
|
|
const updatedConfig = await service.updateViewConfigurations(
|
|
viewConfig.id,
|
|
{
|
|
configuration: {
|
|
visible_columns: ["id", "name", "email"],
|
|
column_order: ["id", "name", "email"],
|
|
filters: { has_account: false }, // Changed filters
|
|
sorting: { id: "created_at", desc: false },
|
|
search: "test search",
|
|
},
|
|
}
|
|
)
|
|
|
|
expect(updatedConfig.configuration).toEqual({
|
|
visible_columns: ["id", "name", "email"],
|
|
column_order: ["id", "name", "email"],
|
|
filters: { has_account: false },
|
|
sorting: { id: "created_at", desc: false },
|
|
search: "test search",
|
|
column_widths: {}, // Default value when not provided
|
|
})
|
|
})
|
|
|
|
it("should update only the name field without affecting configuration", async () => {
|
|
// Create a view with full configuration
|
|
const viewConfig = await service.createViewConfigurations({
|
|
entity: "orders",
|
|
name: "Original Name",
|
|
user_id: "user_123",
|
|
configuration: {
|
|
visible_columns: ["id", "status", "total"],
|
|
column_order: ["id", "status", "total"],
|
|
column_widths: { id: 100, status: 150, total: 200 },
|
|
filters: { status: ["pending", "completed"] },
|
|
sorting: { id: "created_at", desc: true },
|
|
search: "test search",
|
|
},
|
|
})
|
|
|
|
// Update only the name field
|
|
const updatedConfig = await service.updateViewConfigurations(
|
|
viewConfig.id,
|
|
{ name: "Updated Name" }
|
|
)
|
|
|
|
expect(updatedConfig.name).toBe("Updated Name")
|
|
expect(updatedConfig.configuration).toEqual(viewConfig.configuration)
|
|
})
|
|
|
|
it("should completely replace filters when updating configuration", async () => {
|
|
// Create a view with complex filters
|
|
const viewConfig = await service.createViewConfigurations({
|
|
entity: "products",
|
|
name: "Product View",
|
|
user_id: "user_123",
|
|
configuration: {
|
|
visible_columns: ["id", "title"],
|
|
column_order: ["id", "title"],
|
|
filters: {
|
|
status: ["draft", "published"],
|
|
collection_id: ["col_123", "col_456"],
|
|
price_range: { min: 10, max: 100 },
|
|
},
|
|
sorting: { id: "created_at", desc: true },
|
|
},
|
|
})
|
|
|
|
// Update with new filters
|
|
const updatedConfig = await service.updateViewConfigurations(
|
|
viewConfig.id,
|
|
{
|
|
configuration: {
|
|
visible_columns: ["id", "title"],
|
|
column_order: ["id", "title"],
|
|
filters: { category: ["electronics"] }, // Completely different filters
|
|
sorting: { id: "created_at", desc: true },
|
|
},
|
|
}
|
|
)
|
|
|
|
expect(updatedConfig.configuration.filters).toEqual({
|
|
category: ["electronics"],
|
|
})
|
|
// Verify old filters are gone
|
|
expect(updatedConfig.configuration.filters.status).toBeUndefined()
|
|
expect(updatedConfig.configuration.filters.collection_id).toBeUndefined()
|
|
expect(updatedConfig.configuration.filters.price_range).toBeUndefined()
|
|
})
|
|
|
|
it("should remove filters when explicitly set to empty object", async () => {
|
|
// Create a view with filters
|
|
const viewConfig = await service.createViewConfigurations({
|
|
entity: "customers",
|
|
name: "Customer View",
|
|
user_id: "user_123",
|
|
configuration: {
|
|
visible_columns: ["id", "name", "email"],
|
|
column_order: ["id", "name", "email"],
|
|
filters: {
|
|
has_account: true,
|
|
groups: ["vip", "regular"],
|
|
},
|
|
sorting: { id: "created_at", desc: false },
|
|
},
|
|
})
|
|
|
|
// Update configuration with empty filters
|
|
const updatedConfig = await service.updateViewConfigurations(
|
|
viewConfig.id,
|
|
{
|
|
configuration: {
|
|
visible_columns: ["id", "name", "email"],
|
|
column_order: ["id", "name", "email"],
|
|
filters: {}, // Empty filters
|
|
sorting: { id: "created_at", desc: false },
|
|
},
|
|
}
|
|
)
|
|
|
|
expect(updatedConfig.configuration.filters).toEqual({})
|
|
})
|
|
|
|
it("should preserve other configuration properties when updating specific ones", async () => {
|
|
// Create a view with full configuration
|
|
const viewConfig = await service.createViewConfigurations({
|
|
entity: "inventory",
|
|
name: "Inventory View",
|
|
user_id: "user_123",
|
|
configuration: {
|
|
visible_columns: ["id", "sku", "quantity"],
|
|
column_order: ["id", "sku", "quantity"],
|
|
column_widths: { id: 80, sku: 120, quantity: 100 },
|
|
filters: { location: ["warehouse_1"] },
|
|
sorting: { id: "sku", desc: false },
|
|
search: "original search",
|
|
},
|
|
})
|
|
|
|
// Update only filters and sorting
|
|
const updatedConfig = await service.updateViewConfigurations(
|
|
viewConfig.id,
|
|
{
|
|
configuration: {
|
|
visible_columns: viewConfig.configuration.visible_columns,
|
|
column_order: viewConfig.configuration.column_order,
|
|
filters: { location: ["warehouse_2"] },
|
|
sorting: { id: "quantity", desc: true },
|
|
// Note: not providing column_widths and search
|
|
},
|
|
}
|
|
)
|
|
|
|
expect(updatedConfig.configuration.filters).toEqual({
|
|
location: ["warehouse_2"],
|
|
})
|
|
expect(updatedConfig.configuration.sorting).toEqual({
|
|
id: "quantity",
|
|
desc: true,
|
|
})
|
|
// These should be preserved
|
|
expect(updatedConfig.configuration.visible_columns).toEqual(
|
|
viewConfig.configuration.visible_columns
|
|
)
|
|
expect(updatedConfig.configuration.column_order).toEqual(
|
|
viewConfig.configuration.column_order
|
|
)
|
|
// These should have default values since not provided
|
|
expect(updatedConfig.configuration.column_widths).toEqual({})
|
|
expect(updatedConfig.configuration.search).toBe("")
|
|
})
|
|
|
|
it("should handle missing configuration properties with defaults", async () => {
|
|
// Create a view with full configuration
|
|
const viewConfig = await service.createViewConfigurations({
|
|
entity: "payments",
|
|
name: "Payment View",
|
|
user_id: "user_123",
|
|
configuration: {
|
|
visible_columns: ["id", "amount", "status"],
|
|
column_order: ["id", "amount", "status"],
|
|
column_widths: { id: 100, amount: 150, status: 120 },
|
|
filters: { status: ["completed"] },
|
|
sorting: { id: "created_at", desc: true },
|
|
search: "payment search",
|
|
},
|
|
})
|
|
|
|
// Update with partial configuration (only visible_columns)
|
|
const updatedConfig = await service.updateViewConfigurations(
|
|
viewConfig.id,
|
|
{
|
|
configuration: {
|
|
visible_columns: ["id", "amount"],
|
|
column_order: ["id", "amount"],
|
|
// Not providing filters, sorting, search, column_widths
|
|
},
|
|
}
|
|
)
|
|
|
|
expect(updatedConfig.configuration).toEqual({
|
|
visible_columns: ["id", "amount"],
|
|
column_order: ["id", "amount"],
|
|
column_widths: {}, // Default
|
|
filters: {}, // Default
|
|
sorting: null, // Default
|
|
search: "", // Default
|
|
})
|
|
})
|
|
|
|
it("should update multiple view configurations when using selector", async () => {
|
|
// Create multiple views for the same entity
|
|
const view1 = await service.createViewConfigurations({
|
|
entity: "orders",
|
|
name: "Orders View 1",
|
|
user_id: "user_123",
|
|
configuration: {
|
|
visible_columns: ["id", "status"],
|
|
column_order: ["id", "status"],
|
|
filters: { status: ["pending"] },
|
|
},
|
|
})
|
|
|
|
const view2 = await service.createViewConfigurations({
|
|
entity: "orders",
|
|
name: "Orders View 2",
|
|
user_id: "user_456",
|
|
configuration: {
|
|
visible_columns: ["id", "total"],
|
|
column_order: ["id", "total"],
|
|
filters: { status: ["completed"] },
|
|
},
|
|
})
|
|
|
|
const view3 = await service.createViewConfigurations({
|
|
entity: "products", // Different entity
|
|
name: "Products View",
|
|
user_id: "user_123",
|
|
configuration: {
|
|
visible_columns: ["id", "title"],
|
|
column_order: ["id", "title"],
|
|
},
|
|
})
|
|
|
|
// Update using selector for entity "orders"
|
|
const updatedConfigs = await service.updateViewConfigurations(
|
|
{ entity: "orders" },
|
|
{
|
|
configuration: {
|
|
visible_columns: ["id", "status", "total"],
|
|
column_order: ["id", "status", "total"],
|
|
filters: {},
|
|
},
|
|
}
|
|
)
|
|
|
|
// Should return an array
|
|
expect(Array.isArray(updatedConfigs)).toBe(true)
|
|
expect(updatedConfigs).toHaveLength(2)
|
|
|
|
// Both orders views should be updated
|
|
const updatedIds = updatedConfigs.map((v) => v.id).sort()
|
|
expect(updatedIds).toEqual([view1.id, view2.id].sort())
|
|
|
|
// All should have the new configuration
|
|
updatedConfigs.forEach((config) => {
|
|
expect(config.configuration.visible_columns).toEqual([
|
|
"id",
|
|
"status",
|
|
"total",
|
|
])
|
|
expect(config.configuration.filters).toEqual({})
|
|
})
|
|
|
|
// Products view should not be affected
|
|
const productView = await service.retrieveViewConfiguration(view3.id)
|
|
expect(productView.configuration.visible_columns).toEqual(["id", "title"])
|
|
})
|
|
|
|
it("should return empty array when no views match selector", async () => {
|
|
// Try to update with a selector that matches no views
|
|
const result = await service.updateViewConfigurations(
|
|
{ entity: "non_existent_entity" },
|
|
{ name: "New Name" }
|
|
)
|
|
|
|
expect(Array.isArray(result)).toBe(true)
|
|
expect(result).toHaveLength(0)
|
|
})
|
|
|
|
it("should handle null values in configuration", async () => {
|
|
// Create a view with sorting
|
|
const viewConfig = await service.createViewConfigurations({
|
|
entity: "shipping",
|
|
name: "Shipping View",
|
|
user_id: "user_123",
|
|
configuration: {
|
|
visible_columns: ["id", "carrier", "tracking"],
|
|
column_order: ["id", "carrier", "tracking"],
|
|
sorting: { id: "created_at", desc: true },
|
|
search: "fedex",
|
|
},
|
|
})
|
|
|
|
// Update with sorting: null
|
|
const updatedConfig = await service.updateViewConfigurations(
|
|
viewConfig.id,
|
|
{
|
|
configuration: {
|
|
visible_columns: viewConfig.configuration.visible_columns,
|
|
column_order: viewConfig.configuration.column_order,
|
|
sorting: null,
|
|
search: "", // Also test empty string
|
|
},
|
|
}
|
|
)
|
|
|
|
expect(updatedConfig.configuration.sorting).toBeNull()
|
|
expect(updatedConfig.configuration.search).toBe("")
|
|
})
|
|
})
|
|
})
|
|
},
|
|
})
|