describe('User Management Flow', () => { const ADMIN_TOKEN = 'c86ae7afbe10fabe3c1d5e1a7fee74feaadfd5dc7be2ab62' const authHeaders = { Authorization: `Bearer ${ADMIN_TOKEN}` } const TEST_USERNAME = 'cypress_test_user_e2e' const TEST_DISPLAY_NAME = 'Cypress E2E Test User' let testUserId = null describe('API: user lifecycle', () => { // Cleanup any leftover test user first before(() => { cy.request({ url: '/api/users', headers: authHeaders }).then(res => { const leftover = res.body.find(u => u.username === TEST_USERNAME) if (leftover) { cy.request({ method: 'DELETE', url: `/api/users/${leftover.id || leftover._id}`, headers: authHeaders, failOnStatusCode: false }) } }) }) it('creates a new test user via API', () => { cy.request({ method: 'POST', url: '/api/users', headers: authHeaders, body: { username: TEST_USERNAME, display_name: TEST_DISPLAY_NAME, role: 'viewer' } }).then(res => { expect(res.status).to.be.oneOf([200, 201]) testUserId = res.body.id || res.body._id // Should return a token for the new user if (res.body.token) { expect(res.body.token).to.be.a('string') } }) }) it('verifies the user appears in the user list', () => { cy.request({ url: '/api/users', headers: authHeaders }).then(res => { expect(res.body).to.be.an('array') const found = res.body.find(u => u.username === TEST_USERNAME) expect(found).to.exist expect(found.display_name).to.eq(TEST_DISPLAY_NAME) expect(found.role).to.eq('viewer') testUserId = found.id || found._id }) }) it('updates user role to editor', () => { cy.request({ url: '/api/users', headers: authHeaders }).then(res => { const found = res.body.find(u => u.username === TEST_USERNAME) testUserId = found.id || found._id cy.request({ method: 'PUT', url: `/api/users/${testUserId}`, headers: authHeaders, body: { role: 'editor' } }).then(res => { expect(res.status).to.eq(200) }) }) }) it('verifies role was updated', () => { cy.request({ url: '/api/users', headers: authHeaders }).then(res => { const found = res.body.find(u => u.username === TEST_USERNAME) expect(found.role).to.eq('editor') }) }) it('deletes the test user', () => { cy.request({ url: '/api/users', headers: authHeaders }).then(res => { const found = res.body.find(u => u.username === TEST_USERNAME) if (found) { testUserId = found.id || found._id cy.request({ method: 'DELETE', url: `/api/users/${testUserId}`, headers: authHeaders }).then(res => { expect(res.status).to.eq(200) }) } }) }) it('verifies the user is deleted', () => { cy.request({ url: '/api/users', headers: authHeaders }).then(res => { const found = res.body.find(u => u.username === TEST_USERNAME) expect(found).to.not.exist }) }) }) describe('UI: users page renders', () => { it('visits /users and verifies page structure', () => { cy.visit('/users', { onBeforeLoad(win) { win.localStorage.setItem('oil_auth_token', ADMIN_TOKEN) } }) cy.get('.user-management', { timeout: 10000 }).should('exist') cy.contains('用户管理').should('be.visible') }) it('shows search input and role filter buttons', () => { cy.visit('/users', { onBeforeLoad(win) { win.localStorage.setItem('oil_auth_token', ADMIN_TOKEN) } }) cy.get('.user-management', { timeout: 10000 }).should('exist') // Search box cy.get('.search-input').should('exist') // Role filter buttons cy.get('.filter-btn').should('have.length.gte', 1) cy.get('.filter-btn').contains('管理员').should('exist') cy.get('.filter-btn').contains('编辑').should('exist') cy.get('.filter-btn').contains('查看者').should('exist') }) it('displays user list with user cards', () => { cy.visit('/users', { onBeforeLoad(win) { win.localStorage.setItem('oil_auth_token', ADMIN_TOKEN) } }) cy.get('.user-management', { timeout: 10000 }).should('exist') cy.get('.user-card', { timeout: 5000 }).should('have.length.gte', 1) // Each card shows name and role cy.get('.user-card').first().within(() => { cy.get('.user-name').should('not.be.empty') cy.get('.user-role-badge').should('exist') }) }) it('search filters users', () => { cy.visit('/users', { onBeforeLoad(win) { win.localStorage.setItem('oil_auth_token', ADMIN_TOKEN) } }) cy.get('.user-management', { timeout: 10000 }).should('exist') cy.get('.user-card').then($cards => { const total = $cards.length // Search for something specific cy.get('.search-input').type('admin') cy.wait(300) cy.get('.user-card').should('have.length.lte', total) }) }) it('role filter narrows user list', () => { cy.visit('/users', { onBeforeLoad(win) { win.localStorage.setItem('oil_auth_token', ADMIN_TOKEN) } }) cy.get('.user-management', { timeout: 10000 }).should('exist') cy.get('.user-card').then($cards => { const total = $cards.length // Click a role filter cy.get('.filter-btn').contains('管理员').click() cy.wait(300) cy.get('.user-card').should('have.length.lte', total) // Clicking again deactivates the filter cy.get('.filter-btn').contains('管理员').click() cy.wait(300) cy.get('.user-card').should('have.length', total) }) }) it('shows user count', () => { cy.visit('/users', { onBeforeLoad(win) { win.localStorage.setItem('oil_auth_token', ADMIN_TOKEN) } }) cy.get('.user-management', { timeout: 10000 }).should('exist') cy.get('.user-count').should('contain', '个用户') }) it('has create user section', () => { cy.visit('/users', { onBeforeLoad(win) { win.localStorage.setItem('oil_auth_token', ADMIN_TOKEN) } }) cy.get('.user-management', { timeout: 10000 }).should('exist') cy.get('.create-section').should('exist') cy.contains('创建新用户').should('be.visible') }) }) // Safety cleanup after(() => { cy.request({ url: '/api/users', headers: authHeaders, failOnStatusCode: false }).then(res => { if (res.status === 200 && Array.isArray(res.body)) { const testUsers = res.body.filter(u => u.username === TEST_USERNAME) testUsers.forEach(user => { cy.request({ method: 'DELETE', url: `/api/users/${user.id || user._id}`, headers: authHeaders, failOnStatusCode: false }) }) } }) }) })