Implement multi-scheme animation name mapper for Owen Animation System
Some checks failed
CI/CD Pipeline / Test & Lint (16.x) (push) Has been cancelled
CI/CD Pipeline / Test & Lint (18.x) (push) Has been cancelled
CI/CD Pipeline / Test & Lint (20.x) (push) Has been cancelled
CI/CD Pipeline / Security Audit (push) Has been cancelled
CI/CD Pipeline / Release (push) Has been cancelled
Release / Validate Version (push) Has been cancelled
Release / Build and Test (push) Has been cancelled
Release / Create Release (push) Has been cancelled
Release / Publish to NPM (push) Has been cancelled
Release / Deploy Demo (push) Has been cancelled
Animation Processing Pipeline / Validate Animation Names (push) Has been cancelled
Animation Processing Pipeline / Process Blender Animation Assets (push) Has been cancelled
Animation Processing Pipeline / Update Animation Documentation (push) Has been cancelled
Animation Processing Pipeline / Deploy Animation Demo (push) Has been cancelled
Some checks failed
CI/CD Pipeline / Test & Lint (16.x) (push) Has been cancelled
CI/CD Pipeline / Test & Lint (18.x) (push) Has been cancelled
CI/CD Pipeline / Test & Lint (20.x) (push) Has been cancelled
CI/CD Pipeline / Security Audit (push) Has been cancelled
CI/CD Pipeline / Release (push) Has been cancelled
Release / Validate Version (push) Has been cancelled
Release / Build and Test (push) Has been cancelled
Release / Create Release (push) Has been cancelled
Release / Publish to NPM (push) Has been cancelled
Release / Deploy Demo (push) Has been cancelled
Animation Processing Pipeline / Validate Animation Names (push) Has been cancelled
Animation Processing Pipeline / Process Blender Animation Assets (push) Has been cancelled
Animation Processing Pipeline / Update Animation Documentation (push) Has been cancelled
Animation Processing Pipeline / Deploy Animation Demo (push) Has been cancelled
- Added AnimationNameMapper class to handle conversion between different animation naming schemes (legacy, artist, hierarchical, semantic). - Included methods for initialization, pattern matching, conversion, and validation of animation names. - Developed comprehensive unit tests for the animation name converter and demo pages using Playwright. - Created a Vite configuration for the demo application, including asset handling and optimization settings. - Enhanced the demo with features for batch conversion, performance metrics, and responsive design.
This commit is contained in:
158
tests/demo.spec.js
Normal file
158
tests/demo.spec.js
Normal file
@ -0,0 +1,158 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test.describe('Owen Animation System Demo', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Navigate to the demo page before each test
|
||||
await page.goto('/')
|
||||
})
|
||||
|
||||
test('should load the main demo page', async ({ page }) => {
|
||||
// Check that the page title is correct
|
||||
await expect(page).toHaveTitle(/Owen Animation System/)
|
||||
|
||||
// Check that the main heading is present
|
||||
await expect(page.locator('h1')).toContainText('Owen Animation System')
|
||||
|
||||
// Check that the demo content is loaded
|
||||
await expect(page.locator('.demo-content')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should display animation name converter', async ({ page }) => {
|
||||
// Check that the converter section is present
|
||||
await expect(page.locator('.converter-section')).toBeVisible()
|
||||
|
||||
// Check input fields
|
||||
await expect(page.locator('#animationName')).toBeVisible()
|
||||
await expect(page.locator('#sourceScheme')).toBeVisible()
|
||||
await expect(page.locator('#targetScheme')).toBeVisible()
|
||||
|
||||
// Check convert button
|
||||
await expect(page.locator('#convertBtn')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should convert animation names', async ({ page }) => {
|
||||
// Fill in the converter form
|
||||
await page.fill('#animationName', 'char_walk_01')
|
||||
await page.selectOption('#sourceScheme', 'artist')
|
||||
await page.selectOption('#targetScheme', 'semantic')
|
||||
|
||||
// Click convert button
|
||||
await page.click('#convertBtn')
|
||||
|
||||
// Check that result is displayed
|
||||
await expect(page.locator('#conversionResult')).toBeVisible()
|
||||
await expect(page.locator('#conversionResult')).toContainText('character.movement.walk')
|
||||
})
|
||||
|
||||
test('should validate animation names', async ({ page }) => {
|
||||
// Test with invalid animation name
|
||||
await page.fill('#animationName', 'invalid-name-123!@#')
|
||||
await page.selectOption('#sourceScheme', 'semantic')
|
||||
await page.click('#convertBtn')
|
||||
|
||||
// Should show validation error
|
||||
await expect(page.locator('.error-message')).toBeVisible()
|
||||
|
||||
// Test with valid animation name
|
||||
await page.fill('#animationName', 'character.idle.basic')
|
||||
await page.click('#convertBtn')
|
||||
|
||||
// Should show success
|
||||
await expect(page.locator('.success-message')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should show scheme comparison', async ({ page }) => {
|
||||
// Check that scheme cards are present
|
||||
await expect(page.locator('.scheme-card')).toHaveCount(4)
|
||||
|
||||
// Check that each scheme is represented
|
||||
await expect(page.locator('.scheme-card')).toContainText(['Legacy', 'Artist', 'Hierarchical', 'Semantic'])
|
||||
})
|
||||
|
||||
test('should handle batch conversion', async ({ page }) => {
|
||||
// Click on batch conversion tab
|
||||
await page.click('[data-tab="batch"]')
|
||||
|
||||
// Fill in batch input
|
||||
const batchInput = [
|
||||
'char_walk_01',
|
||||
'char_run_02',
|
||||
'prop_door_open'
|
||||
].join('\n')
|
||||
|
||||
await page.fill('#batchInput', batchInput)
|
||||
await page.selectOption('#batchSourceScheme', 'artist')
|
||||
await page.selectOption('#batchTargetScheme', 'semantic')
|
||||
|
||||
// Click convert batch button
|
||||
await page.click('#convertBatchBtn')
|
||||
|
||||
// Check that results are displayed
|
||||
await expect(page.locator('#batchResults')).toBeVisible()
|
||||
await expect(page.locator('.batch-result-item')).toHaveCount(3)
|
||||
})
|
||||
|
||||
test('should export results', async ({ page }) => {
|
||||
// Convert some animation names first
|
||||
await page.fill('#animationName', 'char_walk_01')
|
||||
await page.selectOption('#sourceScheme', 'artist')
|
||||
await page.selectOption('#targetScheme', 'semantic')
|
||||
await page.click('#convertBtn')
|
||||
|
||||
// Wait for result
|
||||
await expect(page.locator('#conversionResult')).toBeVisible()
|
||||
|
||||
// Click export button
|
||||
const downloadPromise = page.waitForEvent('download')
|
||||
await page.click('#exportBtn')
|
||||
const download = await downloadPromise
|
||||
|
||||
// Check that file was downloaded
|
||||
expect(download.suggestedFilename()).toMatch(/animation-conversions.*\.json/)
|
||||
})
|
||||
|
||||
test('should be responsive', async ({ page }) => {
|
||||
// Test mobile viewport
|
||||
await page.setViewportSize({ width: 375, height: 667 })
|
||||
|
||||
// Check that mobile navigation is present
|
||||
await expect(page.locator('.mobile-nav')).toBeVisible()
|
||||
|
||||
// Check that converter still works
|
||||
await page.fill('#animationName', 'test_animation')
|
||||
await page.selectOption('#sourceScheme', 'legacy')
|
||||
await page.selectOption('#targetScheme', 'semantic')
|
||||
await page.click('#convertBtn')
|
||||
|
||||
await expect(page.locator('#conversionResult')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should handle errors gracefully', async ({ page }) => {
|
||||
// Test with empty input
|
||||
await page.click('#convertBtn')
|
||||
await expect(page.locator('.error-message')).toContainText('Animation name is required')
|
||||
|
||||
// Test with same source and target scheme
|
||||
await page.fill('#animationName', 'test_animation')
|
||||
await page.selectOption('#sourceScheme', 'semantic')
|
||||
await page.selectOption('#targetScheme', 'semantic')
|
||||
await page.click('#convertBtn')
|
||||
|
||||
await expect(page.locator('.warning-message')).toContainText('Source and target schemes are the same')
|
||||
})
|
||||
|
||||
test('should show performance metrics', async ({ page }) => {
|
||||
// Check that performance section is present
|
||||
await expect(page.locator('.performance-section')).toBeVisible()
|
||||
|
||||
// Convert some animations to generate metrics
|
||||
await page.fill('#animationName', 'char_walk_01')
|
||||
await page.selectOption('#sourceScheme', 'artist')
|
||||
await page.selectOption('#targetScheme', 'semantic')
|
||||
await page.click('#convertBtn')
|
||||
|
||||
// Check that metrics are updated
|
||||
await expect(page.locator('.conversion-time')).toContainText(/\d+ms/)
|
||||
await expect(page.locator('.total-conversions')).toContainText(/\d+/)
|
||||
})
|
||||
})
|
||||
177
tests/pages.spec.js
Normal file
177
tests/pages.spec.js
Normal file
@ -0,0 +1,177 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test.describe('Examples Page', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/examples.html')
|
||||
})
|
||||
|
||||
test('should load examples page', async ({ page }) => {
|
||||
await expect(page).toHaveTitle(/Examples/)
|
||||
await expect(page.locator('h1')).toContainText('Integration Examples')
|
||||
})
|
||||
|
||||
test('should display framework examples', async ({ page }) => {
|
||||
// Check that example cards are present
|
||||
await expect(page.locator('.example-card')).toHaveCount.greaterThan(3)
|
||||
|
||||
// Check specific framework examples
|
||||
await expect(page.locator('.example-card')).toContainText(['React', 'Vue', 'Node.js'])
|
||||
})
|
||||
|
||||
test('should copy code examples', async ({ page }) => {
|
||||
// Click on a copy button
|
||||
await page.click('.copy-button').first()
|
||||
|
||||
// Check that button text changes to "Copied!"
|
||||
await expect(page.locator('.copy-button').first()).toContainText('Copied!')
|
||||
})
|
||||
|
||||
test('should filter examples', async ({ page }) => {
|
||||
// Click on React filter
|
||||
await page.click('[data-filter="react"]')
|
||||
|
||||
// Check that only React examples are shown
|
||||
await expect(page.locator('.example-card:visible')).toHaveCount(1)
|
||||
await expect(page.locator('.example-card:visible')).toContainText('React')
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Comparison Page', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/comparison.html')
|
||||
})
|
||||
|
||||
test('should load comparison page', async ({ page }) => {
|
||||
await expect(page).toHaveTitle(/Comparison/)
|
||||
await expect(page.locator('h1')).toContainText('Naming Scheme Comparison')
|
||||
})
|
||||
|
||||
test('should display scheme cards', async ({ page }) => {
|
||||
await expect(page.locator('.scheme-card')).toHaveCount(4)
|
||||
|
||||
// Check each scheme is present
|
||||
const schemes = ['Legacy', 'Artist', 'Hierarchical', 'Semantic']
|
||||
for (const scheme of schemes) {
|
||||
await expect(page.locator('.scheme-card')).toContainText(scheme)
|
||||
}
|
||||
})
|
||||
|
||||
test('should show comparison table', async ({ page }) => {
|
||||
await expect(page.locator('.comparison-table')).toBeVisible()
|
||||
|
||||
// Check table headers
|
||||
await expect(page.locator('.comparison-table th')).toContainText(['Animation Name', 'Legacy', 'Artist', 'Hierarchical', 'Semantic'])
|
||||
})
|
||||
|
||||
test('should filter comparison table', async ({ page }) => {
|
||||
// Type in search box
|
||||
await page.fill('.search-input', 'walk')
|
||||
|
||||
// Check that results are filtered
|
||||
await expect(page.locator('.comparison-table tbody tr:visible')).toHaveCount.greaterThan(0)
|
||||
await expect(page.locator('.comparison-table tbody tr:visible')).toContainText('walk')
|
||||
})
|
||||
|
||||
test('should convert between schemes', async ({ page }) => {
|
||||
// Use the conversion demo
|
||||
await page.fill('.animation-input', 'char_walk_01')
|
||||
await page.selectOption('#sourceSchemeSelect', 'artist')
|
||||
await page.selectOption('#targetSchemeSelect', 'semantic')
|
||||
|
||||
// Check conversion result
|
||||
await expect(page.locator('.conversion-result')).toBeVisible()
|
||||
await expect(page.locator('.result-value')).toContainText('character.movement.walk')
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Interactive Playground', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/interactive.html')
|
||||
})
|
||||
|
||||
test('should load interactive page', async ({ page }) => {
|
||||
await expect(page).toHaveTitle(/Interactive/)
|
||||
await expect(page.locator('h1')).toContainText('Interactive Playground')
|
||||
})
|
||||
|
||||
test('should have functional controls', async ({ page }) => {
|
||||
// Check that control sections are present
|
||||
await expect(page.locator('.playground-controls')).toBeVisible()
|
||||
await expect(page.locator('.playground-main')).toBeVisible()
|
||||
await expect(page.locator('.performance-monitor')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should switch between tabs', async ({ page }) => {
|
||||
// Click on different tabs
|
||||
await page.click('[data-tab="converter"]')
|
||||
await expect(page.locator('.tab-content[data-tab="converter"]')).toBeVisible()
|
||||
|
||||
await page.click('[data-tab="validator"]')
|
||||
await expect(page.locator('.tab-content[data-tab="validator"]')).toBeVisible()
|
||||
|
||||
await page.click('[data-tab="generator"]')
|
||||
await expect(page.locator('.tab-content[data-tab="generator"]')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should run code in playground', async ({ page }) => {
|
||||
// Switch to code editor tab
|
||||
await page.click('[data-tab="code"]')
|
||||
|
||||
// Clear and enter new code
|
||||
await page.fill('.code-editor', `
|
||||
const mapper = new AnimationNameMapper();
|
||||
const result = mapper.convert('char_walk_01', 'artist', 'semantic');
|
||||
console.log(result);
|
||||
`)
|
||||
|
||||
// Run the code
|
||||
await page.click('#runCodeBtn')
|
||||
|
||||
// Check output
|
||||
await expect(page.locator('.output-panel')).toContainText('character.movement.walk')
|
||||
})
|
||||
|
||||
test('should validate animation names in real-time', async ({ page }) => {
|
||||
// Enter valid animation name
|
||||
await page.fill('#playgroundAnimationName', 'character.idle.basic')
|
||||
await page.selectOption('#playgroundScheme', 'semantic')
|
||||
|
||||
// Check validation indicator
|
||||
await expect(page.locator('.validation-indicator')).toHaveClass(/success/)
|
||||
|
||||
// Enter invalid animation name
|
||||
await page.fill('#playgroundAnimationName', 'invalid-name-123!')
|
||||
|
||||
// Check validation indicator shows error
|
||||
await expect(page.locator('.validation-indicator')).toHaveClass(/error/)
|
||||
})
|
||||
|
||||
test('should show performance metrics', async ({ page }) => {
|
||||
// Perform some conversions
|
||||
await page.fill('#playgroundAnimationName', 'char_walk_01')
|
||||
await page.selectOption('#playgroundSourceScheme', 'artist')
|
||||
await page.selectOption('#playgroundTargetScheme', 'semantic')
|
||||
await page.click('#convertPlaygroundBtn')
|
||||
|
||||
// Check that performance metrics are updated
|
||||
await expect(page.locator('.monitor-value')).toHaveCount.greaterThan(0)
|
||||
await expect(page.locator('.conversion-time .monitor-value')).toContainText(/\d+/)
|
||||
})
|
||||
|
||||
test('should save and load history', async ({ page }) => {
|
||||
// Perform a conversion
|
||||
await page.fill('#playgroundAnimationName', 'test_animation')
|
||||
await page.selectOption('#playgroundSourceScheme', 'legacy')
|
||||
await page.selectOption('#playgroundTargetScheme', 'semantic')
|
||||
await page.click('#convertPlaygroundBtn')
|
||||
|
||||
// Check that history is updated
|
||||
await expect(page.locator('.history-item')).toHaveCount.greaterThan(0)
|
||||
|
||||
// Click on history item to load it
|
||||
await page.click('.history-item').first()
|
||||
|
||||
// Check that form is populated
|
||||
await expect(page.locator('#playgroundAnimationName')).toHaveValue('test_animation')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user