fix: preserve comment IDs and enter edit mode only on request
Deploy EG-BIM QA Gateway / deploy (push) Failing after 21s
Deploy EG-BIM QA Gateway / deploy (push) Failing after 21s
This commit is contained in:
1 parent
922d24cac7
commit
03f2cd3b65
2 files changed
+157
-8
No files matched your search
@@ -0,0 +1,143 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import vm from 'node:vm';
|
||||
|
||||
const source = readFileSync(new URL('../egbim/app.js', import.meta.url), 'utf8');
|
||||
const POST = '01900000-0000-7000-8000-000000000001';
|
||||
const FIRST = '01900000-0000-7000-8000-000000000002';
|
||||
const SECOND = '01900000-0000-7000-8000-000000000003';
|
||||
const USER = { requesterId: 'owner', ssoSubject: 'owner', name: '작성자 이름' };
|
||||
const COMMENTS_KEY = 'baron.qa.comments.egbim';
|
||||
const comment = (extra = {}) => ({ id: FIRST, author_id: USER.requesterId, author_name: USER.name, content: '원래 댓글', created_at: '2026-10-29T04:14:00Z', ...extra });
|
||||
|
||||
function element() {
|
||||
return {
|
||||
innerHTML: '', textContent: '', value: '', hidden: false, disabled: false,
|
||||
listeners: {}, classList: { toggle() {}, add() {}, remove() {} },
|
||||
addEventListener(name, callback) { this.listeners[name] = callback; },
|
||||
querySelector() { return null; },
|
||||
focus() {}, setSelectionRange() {}
|
||||
};
|
||||
}
|
||||
|
||||
async function detailPage({ comments = [], cachedComments = [], createResponse = {}, patchResponse = {}, authRefresh = false } = {}) {
|
||||
const elements = new Map([
|
||||
'#detail', '[data-detail-category]', '[data-detail-title]', '[data-detail-status]',
|
||||
'[data-detail-date]', '[data-detail-author]', '[data-detail-company]',
|
||||
'[data-detail-department]', '[data-detail-content]', '[data-detail-secret]',
|
||||
'#comments', '#commentCount', '#commentForm', '#commentContent',
|
||||
'#commentError', '#commentSubmit', '[data-comment-edit-input]'
|
||||
].map((selector) => [selector, element()]));
|
||||
const storage = new Map([[COMMENTS_KEY, JSON.stringify({ [POST]: cachedComments })]]);
|
||||
const requests = [];
|
||||
const context = vm.createContext({
|
||||
window: { QA_CONFIG: {}, location: { origin: 'https://qa.example.test', search: '?id=' + POST }, confirm: () => true },
|
||||
document: { cookie: '', addEventListener() {}, querySelector: (selector) => elements.get(selector) || null },
|
||||
sessionStorage: { getItem: () => null },
|
||||
localStorage: { getItem: (key) => storage.get(key) || null, setItem: (key, value) => storage.set(key, value) },
|
||||
URL, URLSearchParams, console,
|
||||
async fetch(path, options) {
|
||||
requests.push({ path, ...options });
|
||||
const method = options.method || 'GET';
|
||||
let body;
|
||||
if (method === 'POST') body = createResponse;
|
||||
else if (method === 'PATCH') body = patchResponse;
|
||||
else if (method === 'DELETE') body = { ok: true };
|
||||
else if (path.endsWith('/comments')) body = { comments };
|
||||
else body = { id: POST, title: '문의 제목', contents: '문의 내용', requester_id: USER.requesterId };
|
||||
return new Response(JSON.stringify(body));
|
||||
}
|
||||
});
|
||||
vm.runInContext(source.replace(/\}\)\(\);\s*$/, `
|
||||
globalThis.testPage = { initDetail, setUser(user, refresh) {
|
||||
resolvedAuthUser = user;
|
||||
if (refresh) authReady = Promise.resolve(user);
|
||||
} }; })();`), context);
|
||||
context.testPage.setUser(USER, authRefresh);
|
||||
await context.testPage.initDetail();
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
return {
|
||||
elements, requests,
|
||||
html: () => elements.get('#comments').innerHTML,
|
||||
stored: () => JSON.parse(storage.get(COMMENTS_KEY))[POST],
|
||||
async click(action, id = FIRST, value = '') {
|
||||
elements.get('[data-comment-edit-input]').value = value;
|
||||
const button = { disabled: false, getAttribute: () => id };
|
||||
await elements.get('#comments').listeners.click({ target: { closest: (selector) => selector === '[data-comment-' + action + ']' ? button : null } });
|
||||
},
|
||||
async create(content) {
|
||||
elements.get('#commentContent').value = content;
|
||||
await elements.get('#commentForm').listeners.submit({ preventDefault() {} });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test('initial comments show text and owner actions, and never auto-edit a comment without an ID', async () => {
|
||||
const page = await detailPage({ comments: [comment(), comment({ id: '', content: 'ID 없는 댓글' }), comment({ id: SECOND, author_id: 'another-user' })] });
|
||||
assert.match(page.html(), /class="comment-content">ID 없는 댓글<\/div>/);
|
||||
assert.match(page.html(), new RegExp('data-comment-edit="' + FIRST + '"'));
|
||||
assert.match(page.html(), new RegExp('data-comment-delete="' + FIRST + '"'));
|
||||
assert.doesNotMatch(page.html(), /data-comment-edit-input|data-comment-save|data-comment-edit=""|data-comment-delete=""/);
|
||||
assert.doesNotMatch(page.html(), new RegExp('data-comment-(edit|delete)="' + SECOND + '"'));
|
||||
});
|
||||
|
||||
for (const idField of ['id', 'comment_id', 'commentId']) {
|
||||
test(`a ${idField} response keeps its ID and date through edit, cancel, save, and delete`, async () => {
|
||||
const page = await detailPage({
|
||||
comments: [comment({ id: undefined, [idField]: FIRST }), comment({ id: SECOND, content: '다른 댓글' })],
|
||||
patchResponse: { comment: { content: '수정한 댓글' } }, authRefresh: true
|
||||
});
|
||||
assert.doesNotMatch(page.html(), /data-comment-edit-input/);
|
||||
assert.match(page.html(), new RegExp('data-comment-edit="' + FIRST + '"'));
|
||||
await page.click('edit');
|
||||
assert.match(page.html(), /data-comment-edit-input/);
|
||||
assert.match(page.html(), /class="comment-content">다른 댓글<\/div>/);
|
||||
assert.equal((page.html().match(/data-comment-edit-input/g) || []).length, 1);
|
||||
await page.click('cancel');
|
||||
assert.doesNotMatch(page.html(), /data-comment-edit-input/);
|
||||
assert.match(page.html(), new RegExp('data-comment-delete="' + FIRST + '"'));
|
||||
await page.click('edit');
|
||||
await page.click('save', FIRST, '수정한 댓글');
|
||||
assert.doesNotMatch(page.html(), /data-comment-edit-input/);
|
||||
assert.match(page.html(), /class="comment-content">수정한 댓글<\/div>/);
|
||||
assert.match(page.html(), /2026-10-29 13:14/);
|
||||
assert.equal(page.stored()[0].id, FIRST);
|
||||
assert.equal(page.stored()[0].authorId, USER.requesterId);
|
||||
const patch = page.requests.find((request) => request.method === 'PATCH');
|
||||
assert.equal(patch.path, '/api/feedbacks/' + POST + '/comments/' + FIRST);
|
||||
assert.deepEqual(JSON.parse(patch.body), { content: '수정한 댓글' });
|
||||
await page.click('delete');
|
||||
assert.doesNotMatch(page.html(), /수정한 댓글/);
|
||||
assert.match(page.html(), /다른 댓글/);
|
||||
assert.equal(page.requests.find((request) => request.method === 'DELETE').path, patch.path);
|
||||
});
|
||||
}
|
||||
|
||||
for (const createResponse of [
|
||||
{ id: FIRST, comment: { content: '등록한 댓글' } },
|
||||
{ comment: { comment_id: FIRST, content: '등록한 댓글' } },
|
||||
{ data: { commentId: FIRST, content: '등록한 댓글' } },
|
||||
{ id: FIRST }
|
||||
]) {
|
||||
test(`a newly created comment displays owner actions using ${JSON.stringify(createResponse)}`, async () => {
|
||||
const page = await detailPage({ createResponse });
|
||||
await page.create('등록한 댓글');
|
||||
assert.match(page.html(), /class="comment-content">등록한 댓글<\/div>/);
|
||||
assert.match(page.html(), new RegExp('data-comment-edit="' + FIRST + '"'));
|
||||
assert.match(page.html(), new RegExp('data-comment-delete="' + FIRST + '"'));
|
||||
assert.doesNotMatch(page.html(), /data-comment-edit-input/);
|
||||
assert.equal(page.stored()[0].id, FIRST);
|
||||
assert.equal(page.stored()[0].author, USER.name);
|
||||
await page.click('edit');
|
||||
await page.click('cancel');
|
||||
assert.ok(page.stored()[0].date);
|
||||
});
|
||||
}
|
||||
|
||||
test('a cached comment without an ID remains read-only when loading the page', async () => {
|
||||
const page = await detailPage({ cachedComments: [comment({ id: '' })], comments: [comment({ id: '' })], authRefresh: true });
|
||||
assert.match(page.html(), /class="comment-content">원래 댓글<\/div>/);
|
||||
assert.doesNotMatch(page.html(), /data-comment-edit|data-comment-delete|data-comment-save/);
|
||||
assert.equal(page.requests.some((request) => ['PATCH', 'DELETE'].includes(request.method)), false);
|
||||
});
|
||||
Reference in new issue
Block a user