diff --git a/fakemui/icons/FilterOff.tsx b/fakemui/icons/FilterOff.tsx
new file mode 100644
index 000000000..e9752dc15
--- /dev/null
+++ b/fakemui/icons/FilterOff.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import { Icon, IconProps } from './Icon'
+
+export const FilterOff = (props: IconProps) => (
+
+
+
+
+
+)
diff --git a/fakemui/icons/Flag.tsx b/fakemui/icons/Flag.tsx
new file mode 100644
index 000000000..035068a76
--- /dev/null
+++ b/fakemui/icons/Flag.tsx
@@ -0,0 +1,9 @@
+import React from 'react'
+import { Icon, IconProps } from './Icon'
+
+export const Flag = (props: IconProps) => (
+
+
+
+
+)
diff --git a/fakemui/icons/ShieldSlash.tsx b/fakemui/icons/ShieldSlash.tsx
new file mode 100644
index 000000000..d5402fc50
--- /dev/null
+++ b/fakemui/icons/ShieldSlash.tsx
@@ -0,0 +1,9 @@
+import React from 'react'
+import { Icon, IconProps } from './Icon'
+
+export const ShieldSlash = (props: IconProps) => (
+
+
+
+
+)
diff --git a/fakemui/icons/index.ts b/fakemui/icons/index.ts
index 20e01738d..6814d0fa9 100644
--- a/fakemui/icons/index.ts
+++ b/fakemui/icons/index.ts
@@ -16,6 +16,7 @@ export { Stop } from './Stop'
export { Download } from './Download'
export { Upload } from './Upload'
export { Filter } from './Filter'
+export { FilterOff } from './FilterOff'
export { Refresh } from './Refresh'
// Navigation
@@ -138,6 +139,7 @@ export { LockOpen } from './LockOpen'
export { Key } from './Key'
export { Shield } from './Shield'
export { UserShield } from './UserShield'
+export { ShieldSlash } from './ShieldSlash'
export { Globe } from './Globe'
export { Cloud } from './Cloud'
diff --git a/fakemui/index.ts b/fakemui/index.ts
index 4e7ca736b..6bb78dcd0 100644
--- a/fakemui/index.ts
+++ b/fakemui/index.ts
@@ -10,6 +10,8 @@ export {
Copy,
Check,
X,
+ Filter,
+ FilterOff,
// Navigation
ArrowUp,
ArrowDown,
diff --git a/packages/audit_log/seed/tests/filters.test.lua b/packages/audit_log/seed/tests/filters.test.lua
index 24c03968d..9beaac8c4 100644
--- a/packages/audit_log/seed/tests/filters.test.lua
+++ b/packages/audit_log/seed/tests/filters.test.lua
@@ -20,6 +20,14 @@ local M = {}
---@field resources string[]
---@field usernames string[]
+---@class ApplyFiltersInput
+---@field operation string|nil
+---@field resource string|nil
+---@field success boolean|nil
+---@field username string|nil
+---@field startTime number|nil
+---@field endTime number|nil
+
---@class FilterByOperationCase
---@field operation string|nil
---@field expected integer
diff --git a/packages/nav_menu/seed/scripts/tests/menu.cases.json b/packages/nav_menu/seed/scripts/tests/menu.cases.json
new file mode 100644
index 000000000..b4a262200
--- /dev/null
+++ b/packages/nav_menu/seed/scripts/tests/menu.cases.json
@@ -0,0 +1,48 @@
+{
+ "can_show": [
+ {
+ "user": { "level": 0 },
+ "item": { "label": "Home" },
+ "expected": true,
+ "desc": "item with no minLevel"
+ },
+ {
+ "user": { "level": 0 },
+ "item": { "label": "Admin", "minLevel": 3 },
+ "expected": false,
+ "desc": "low level user for admin"
+ },
+ {
+ "user": { "level": 3 },
+ "item": { "label": "Admin", "minLevel": 3 },
+ "expected": true,
+ "desc": "admin for admin item"
+ },
+ {
+ "user": { "level": 5 },
+ "item": { "label": "Admin", "minLevel": 3 },
+ "expected": true,
+ "desc": "high level for admin item"
+ },
+ {
+ "user": {},
+ "item": { "label": "Users", "minLevel": 2 },
+ "expected": false,
+ "desc": "no level user"
+ }
+ ],
+ "render": [
+ {
+ "props": {
+ "user": { "level": 2 },
+ "items": [
+ { "label": "Home", "path": "/" },
+ { "label": "Admin", "path": "/admin", "minLevel": 3 },
+ { "label": "Users", "path": "/users", "minLevel": 2 }
+ ]
+ },
+ "expectedChildren": 2,
+ "desc": "filters items by permission"
+ }
+ ]
+}
diff --git a/packages/nav_menu/seed/scripts/tests/menu.test.lua b/packages/nav_menu/seed/scripts/tests/menu.test.lua
index 43856bcf8..d6e55b79f 100644
--- a/packages/nav_menu/seed/scripts/tests/menu.test.lua
+++ b/packages/nav_menu/seed/scripts/tests/menu.test.lua
@@ -14,15 +14,21 @@
---@field user MenuUser
---@field items MenuItem[]
----@class MenuShowTestCase
----@field user MenuUser
----@field item MenuItem
----@field expected boolean
----@field desc string
-
-describe("Menu", function()
- -- Mock check module
- local original_can_access
+---@class MenuShowTestCase
+---@field user MenuUser
+---@field item MenuItem
+---@field expected boolean
+---@field desc string
+
+---@class MenuRenderTestCase
+---@field props MenuRenderProps
+---@field expectedChildren integer
+---@field desc string
+
+describe("Menu", function()
+ -- Mock check module
+ ---@type { can_show: MenuShowTestCase[], render: MenuRenderTestCase[] }
+ local cases = load_cases("menu.cases.json")
before(function()
-- Create mock for check.can_access
@@ -33,20 +39,14 @@ describe("Menu", function()
}
end)
- local menu = require("menu")
-
- describe("can_show", function()
- it.each({
- { user = { level = 0 }, item = { label = "Home" }, expected = true, desc = "item with no minLevel" },
- { user = { level = 0 }, item = { label = "Admin", minLevel = 3 }, expected = false, desc = "low level user for admin" },
- { user = { level = 3 }, item = { label = "Admin", minLevel = 3 }, expected = true, desc = "admin for admin item" },
- { user = { level = 5 }, item = { label = "Admin", minLevel = 3 }, expected = true, desc = "high level for admin item" },
- { user = {}, item = { label = "Users", minLevel = 2 }, expected = false, desc = "no level user" },
- })("should return $expected for $desc", function(testCase)
- local result = menu.can_show(testCase.user, testCase.item)
- expect(result).toBe(testCase.expected)
- end)
- end)
+ local menu = require("menu")
+
+ describe("can_show", function()
+ it.each(cases.can_show, "$desc", function(testCase)
+ local result = menu.can_show(testCase.user, testCase.item)
+ expect(result).toBe(testCase.expected)
+ end)
+ end)
describe("item", function()
it("should render button for simple item", function()
@@ -69,19 +69,11 @@ describe("Menu", function()
end)
end)
- describe("render", function()
- it("should filter items by permission", function()
- local props = {
- user = { level = 2 },
- items = {
- { label = "Home", path = "/" },
- { label = "Admin", path = "/admin", minLevel = 3 },
- { label = "Users", path = "/users", minLevel = 2 }
- }
- }
- local result = menu.render(props)
- expect(result.type).toBe("Flex")
- expect(#result.children).toBe(2) -- Home and Users, not Admin
- end)
- end)
-end)
+ describe("render", function()
+ it.each(cases.render, "$desc", function(testCase)
+ local result = menu.render(testCase.props)
+ expect(result.type).toBe("Flex")
+ expect(#result.children).toBe(testCase.expectedChildren)
+ end)
+ end)
+end)
diff --git a/packages/ui_level3/seed/scripts/moderation/types.lua b/packages/ui_level3/seed/scripts/moderation/types.lua
index 0e6fc5d5f..191f85be8 100644
--- a/packages/ui_level3/seed/scripts/moderation/types.lua
+++ b/packages/ui_level3/seed/scripts/moderation/types.lua
@@ -1,9 +1,13 @@
-- Type definitions for moderation module
-- Shared across all moderation functions
----@class ModerationContext
----@field user table User object for permission checking
----@field targetId string ID of the target user for moderation action
+---@class ModerationUser
+---@field id string
+---@field level? number
+
+---@class ModerationContext
+---@field user ModerationUser User object for permission checking
+---@field targetId string ID of the target user for moderation action
---@class ActionResult
---@field success boolean Whether the action was successful