테스트코드 삭제

This commit is contained in:
2026-09-23 16:00:28 +09:00
parent 7cbb58ff32
commit d45cd79422
11 changed files with 9 additions and 294 deletions
+1 -1
View File
@@ -228,7 +228,7 @@ VSCode나 별도 터미널에서 `sn` Next 개발 서버가 실행 중이면 `ne
### 프론트엔드 실행 프로세스 관리
- 모든 세션·역할·문서/검증 섹션에 공통 D:/workspace/AGENTS.md의 테스트 임시 파일·결과 파일 정리 기준을 적용한다. 일회성 검증 스크립트·캡처·Network/서버 로그·원시 결과 파일은 최종 보고서에 요약 후 삭제한다. 정식 회귀 테스트·최종 산출물·사용자 원본/복구 백업은 유지한다. 사용자 dev server가 쓰는 .next·공유 캐시·node_modules는 임시 검증물이라는 이유로 포괄 삭제하지 않는다.
- 모든 세션·역할·문서/검증 섹션에 공통 D:/workspace/AGENTS.md의 테스트 파일 정리 기준을 적용한다. 에이전트가 테스트를 위해 만든 파일은 정식 회귀 테스트·재사용 가능한 소스·fixture·mock·임시 라우트를 포함해 위치와 관계없이 결과를 요약한 뒤 작업 종료 전에 삭제한다(2026-09-23 사용자 지시). 전용 빈 폴더와 끊어진 실행 명령도 정리한다. 명시 보존 요청 파일·최종 산출물·사용자 원본/복구 백업은 유지한다. 기존 타 작업자 테스트 자산은 삭제 권한을 먼저 확인한다. 사용자 dev server의 .next·공유 캐시·node_modules는 포괄 삭제하지 않는다.
- Codex 에이전트가 작업 또는 검증을 위해 직접 기동한 `next dev`, `dev:sn`, Turborepo dev와 기타 프론트엔드 장시간 실행 프로세스는 작업 종료 전에 반드시 종료한다.
- 셸, 백그라운드, 숨김 창 등 VSCode 밖에서 에이전트가 기동한 프로세스도 종료 대상이다.
@@ -1,105 +0,0 @@
"use client";
import React from "react";
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
type ChartOptions,
} from "chart.js";
import { Bar } from "react-chartjs-2";
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
export default function Page() {
const labels = ["한강", "낙동강", "영산강", "금강"];
// 데이터: [기타, 오수관로, 우수관로]
const datasets = [
{
label: "기타",
data: [15481, 6959, 1938, 1967],
backgroundColor: "rgba(255, 178, 150, 0.9)", // 연한 주황(파스텔톤)
borderColor: "rgba(255,178,150,1)",
},
{
label: "오수관로",
data: [15878, 16336, 5670, 8073],
backgroundColor: "rgba(180, 240, 190, 0.95)", // 연한 초록(파스텔톤)
borderColor: "rgba(180,240,190,1)",
},
{
label: "우수관로",
data: [12892, 10491, 4280, 4838],
backgroundColor: "rgba(170, 210, 245, 0.95)", // 연한 파랑(파스텔톤)
borderColor: "rgba(170,210,245,1)",
},
];
// 총합(라벨당) 계산
const totals = labels.map((_, i) =>
datasets.reduce((s, ds) => s + (ds.data[i] as number), 0),
);
const options: ChartOptions<"bar"> = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "top",
},
tooltip: {
callbacks: {
label: (ctx) => `${ctx.dataset.label}: ${Number(ctx.parsed.y).toLocaleString()}km`,
},
},
},
scales: {
x: {
stacked: true,
},
y: {
stacked: true,
beginAtZero: true,
ticks: {
callback: (value) => `${Number(value).toLocaleString()}km`,
},
},
},
// 각 스택 위에 총합 표시
animation: {
onComplete: function (animation) {
const chart = animation.chart;
const ctx = chart.ctx;
const lastMeta = chart.getDatasetMeta(datasets.length - 1);
ctx.save();
ctx.fillStyle = "#111";
ctx.font = "gray bold 12px Arial";
ctx.textAlign = "center";
totals.forEach((total, i) => {
const bar = lastMeta.data[i];
if (!bar) return;
const x = bar.x;
// y는 마지막 스택의 y 좌표(스택의 최상단)에서 조금 위로
const y = bar.y - 8;
ctx.fillText(`총 ${total.toLocaleString()}km`, x, y);
});
ctx.restore();
},
},
};
const data = { labels, datasets };
return (
<div style={{ width: "100%", maxWidth: 800, height: 360 }}>
<Bar options={options} data={data} />
</div>
);
}
@@ -1,5 +1,7 @@
# SN 전체 목록 그리드 말줄임표
> 2026-09-23 후속 정리: 사용자 요청에 따라 `scripts/grid-cell-tooltip.test.cjs`를 삭제했다. 아래 테스트 명령과 결과는 삭제 전 실행 이력이며 테스트 소스 보존 문구는 더 이상 적용하지 않는다. 구현 코드와 이 최종 보고서는 유지한다.
2026-09-23 사용자 요청: 노후 하수관로 정비사업 화면처럼 긴 내용이 행 높이를 늘리는 현상을 SN 전체 그리드에서 정리한다.
기존 원본 화면의 표 컬럼·업무값은 유지하고 이번 요청을 표시 방식 변경 근거로 적용했다.
@@ -1,5 +1,7 @@
# SN 그리드 표시 순번 오름차순 통일
> 2026-09-23 후속 정리: 사용자 요청에 따라 `scripts/row-number.test.cjs`를 삭제했다. 아래 테스트 명령과 결과는 삭제 전 실행 이력이며 테스트 소스 보존 문구는 더 이상 적용하지 않는다. 구현 코드와 이 최종 보고서는 유지한다.
## 목적 및 기준
2026-09-23 사용자 지시: 하수관로 SN 전체 그리드의 표시 순번을 1, 2, 3…으로 통일한다.
@@ -1,5 +1,7 @@
# 노후관로 목록 조회조건 및 표시항목 변경
> 2026-09-23 후속 정리: 사용자 요청에 따라 `scripts/aged-pipe-list.test.cjs`를 삭제했다. 아래 테스트 명령과 결과는 삭제 전 실행 이력이며 테스트 소스 보존 문구는 더 이상 적용하지 않는다. 구현 코드와 이 최종 보고서는 유지한다.
## 목적과 근거
- 2026-09-23 사용자 요청: 조회조건을 시도, 시군구, 경과년도 순으로 변경하고 관리기관, 설치일자, 경과년도를 연속 배치한다.
@@ -1,5 +1,7 @@
# 하수관망도 레이어 전체 켜기·끄기
> 2026-09-23 후속 정리: 사용자 요청에 따라 `scripts/layer-bulk-selection.test.cjs`를 삭제했다. 아래 테스트 명령과 결과는 삭제 전 실행 이력이며 테스트 소스 보존 문구는 더 이상 적용하지 않는다. 구현 코드와 이 최종 보고서는 유지한다.
2026-09-23 사용자 요청으로 트리의 '레이어 관리' 제목 오른쪽에 전체 표시 토글을 추가했다.
후속 요청에 따라 두 버튼을 단일 스위치로 바꿨다. 모두 선택이면 ON, 일부 선택이면 OFF 위치와 '일부 켜짐' 문구로 표시한다.
OFF 또는 부분 선택 상태에서 누르면 전체 켜기, ON 상태에서 누르면 전체 끄기가 실행된다.
-2
View File
@@ -8,9 +8,7 @@
"build": "next build",
"start:sn": "next start -p 3002",
"lint": "next lint",
"test:refactor": "node scripts/run-refactor-tests.cjs",
"typecheck": "tsc --noEmit --incremental false --pretty false",
"check:boundaries": "node scripts/check-module-boundaries.cjs",
"lint:refactor": "eslint src/shared/api/http-transport.ts src/app/providers/app-provider.tsx src/features/dredging src/features/mapLayers src/features/gisManagement --rule \"@typescript-eslint/no-explicit-any:warn\" --max-warnings 22"
},
"dependencies": {
-64
View File
@@ -1,64 +0,0 @@
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const test = require('node:test');
const ts = require('typescript');
const directory = path.resolve(__dirname, '../app/(map-layout)/map/components/agedPipe');
const source = fs.readFileSync(path.join(directory, 'agedPipeUtils.ts'), 'utf8');
const compiled = ts.transpileModule(source, {
compilerOptions: { module: ts.ModuleKind.CommonJS },
}).outputText;
const exportsValue = {};
vm.runInNewContext(compiled, {
exports: exportsValue,
require: (id) => {
if (id.endsWith('/formatDisplayValue')) return { formatDisplayData: (row) => row };
if (id === '../common/table') return { MS_SWAGE_PPL_CODES: [], MS_SWAGE_PPL_USE_CODES: [] };
throw new Error(`Unexpected import: ${id}`);
},
});
const { AGED_PIPE_LIST_HEADLIST: headers, getAgedPipeManagementName: name } = exportsValue;
test('list preserves existing columns and groups agency, installation date and age', () => {
assert.deepEqual(Array.from(headers, (column) => column.id), [
'rowNumber', 'fcltMngNo', 'mngInstNm', 'instlYmd', 'elapsedYears',
'pdmtVl', 'prlgVl', 'usgCd', 'mqltCd',
]);
assert.equal(headers.reduce((total, column) => total + column.widthPercent, 0), 100);
});
test('agency name uses existing API names without changing source codes', () => {
const agencies = [{ mngInstCd: '123', lwrInstNm: '검증기관' }];
const row = { mngInstCd: '123' };
assert.equal(name(row, agencies), '검증기관');
assert.equal(row.mngInstCd, '123');
assert.equal(name({ mng_inst_cd: ' 123 ' }, agencies), '검증기관');
assert.equal(name({ mngInstNm: '원본기관', mngInstCd: '123' }, agencies), '원본기관');
assert.equal(name({ mngInstNm: '-', mngInstCd: '123' }, agencies), '검증기관');
assert.equal(name({ mngInstCd: '999' }, agencies), '999');
assert.equal(name({}, agencies), '-');
});
test('search controls are ordered province, municipality, elapsed years', () => {
const page = fs.readFileSync(path.join(directory, 'AgedPipePopupPage.tsx'), 'utf8');
const panel = page.slice(page.indexOf('<PopupSearchPanel'), page.indexOf('</PopupSearchPanel>'));
const labels = Array.from(panel.matchAll(/<span[^>]*>\s*([^<]+?)\s*<\/span>/g), (match) => match[1].trim());
assert.deepEqual(labels, ['시도', '시군구', '경과년도']);
});
test('aged pipe popup uses the common centered placement without changing its size', () => {
const definitions = fs.readFileSync(path.resolve(__dirname, '../src/features/windowPopup/model/popupDefinitions.ts'), 'utf8');
const exported = {};
vm.runInNewContext(ts.transpileModule(definitions, {
compilerOptions: { module: ts.ModuleKind.CommonJS },
}).outputText, { exports: exported });
const popup = exported.windowPopupDefinitions.agedPipePopup;
assert.equal(popup.width, 1160);
assert.equal(popup.height, 820);
assert.equal(popup.placement ?? 'center', 'center');
assert.equal(popup.alignTop ?? false, false);
assert.equal(popup.offsetLeft ?? 0, 0);
assert.equal(popup.offsetTop ?? 0, 0);
});
@@ -1,48 +0,0 @@
const assert = require('node:assert/strict');
const test = require('node:test');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const ts = require('typescript');
const root = path.resolve(__dirname, '..');
const source = fs.readFileSync(path.join(root, 'src/shared/ui/grid-cell-tooltip.tsx'), 'utf8');
const css = fs.readFileSync(path.join(root, 'src/app/styles/grid-ellipsis.css'), 'utf8');
function mount() {
const handlers = {};
let cleanup;
class Cell {
constructor(text, overflowing = true) { this.textContent = text; this.title = ''; this.clientWidth = 100; this.scrollWidth = overflowing ? 300 : 100; }
closest() { return this; }
querySelector() { return null; }
querySelectorAll() { return []; }
hasAttribute() { return Boolean(this.title); }
removeAttribute() { this.title = ''; }
}
const module = {exports:{}};
vm.runInNewContext(ts.transpileModule(source, {compilerOptions:{module:ts.ModuleKind.CommonJS}}).outputText, {
module, exports:module.exports, Element:Cell,
require:()=>({useEffect:fn=>{cleanup=fn();}}),
document:{addEventListener:(name,handler)=>{handlers[name]=handler;},removeEventListener:name=>{delete handlers[name];}},
});
module.exports.GridCellTooltip();
return {Cell,handlers,cleanup};
}
test('CSS and tooltip target the same SN text cells and preserve controls', () => {
const selector = source.match(/const CELL_SELECTOR = '([^']+)'/)[1];
assert.ok(css.includes(selector + ' {'));
for (const token of ['input','textarea','button','[colspan]','[rowspan]','data-sn-cell-wrap']) assert.ok(selector.includes(token));
});
test('overflow tooltip retains full text and updates reused cells', () => {
const {Cell,handlers,cleanup}=mount(); const cell=new Cell('긴 원문\n내용');
handlers.pointerover({target:cell}); assert.equal(cell.title,cell.textContent);
cell.textContent='변경된 원문'; handlers.pointerover({target:cell}); assert.equal(cell.title,cell.textContent);
cell.scrollWidth=100; handlers.pointerover({target:cell}); assert.equal(cell.title,'');
cleanup(); assert.deepEqual(handlers,{});
});
test('existing explicit tooltip and editable-cell exclusions remain authoritative', () => {
const {Cell,handlers}=mount(); const cell=new Cell('원문'); cell.title='기존 설명';
handlers.pointerover({target:cell}); assert.equal(cell.title,'기존 설명');
const excluded=new Cell('입력'); excluded.closest=()=>null;
handlers.pointerover({target:excluded}); assert.equal(excluded.title,'');
});
@@ -1,30 +0,0 @@
const assert = require('node:assert/strict');
const test = require('node:test');
const { pathToFileURL } = require('node:url');
const path = require('node:path');
const moduleUrl = pathToFileURL(path.resolve(__dirname, '../../../packages/common/business_components/src/ui/map/layerBulkSelection.js'));
test('toggle state follows all, partial, off and empty selections', async () => {
const { getBulkLayerState } = await import(moduleUrl.href);
const nodes = [{id:'a',data:{exprsYn:'Y'}},{id:'b',data:{exprsYn:'Y'}},{id:'h',data:{exprsYn:'N'}}];
assert.deepEqual(getBulkLayerState(nodes, []), {disabled:false,allChecked:false,partiallyChecked:false});
assert.deepEqual(getBulkLayerState(nodes, ['a']), {disabled:false,allChecked:false,partiallyChecked:true});
assert.deepEqual(getBulkLayerState(nodes, ['a','b']), {disabled:false,allChecked:true,partiallyChecked:false});
assert.deepEqual(getBulkLayerState([], []), {disabled:true,allChecked:false,partiallyChecked:false});
});
test('bulk on checks visible parents and children, excluding hidden nodes', async () => {
const { getBulkLayerSelection } = await import(moduleUrl.href);
const nodes = [{id:1,data:{exprsYn:'Y'}},{id:2,parent:1,data:{exprsYn:'Y'}},{id:3,data:{exprsYn:'N'}}];
const result = getBulkLayerSelection(nodes, true);
assert.deepEqual(result.checkedIds, [1,2]);
assert.deepEqual(result.uncheckedIds, [3]);
assert.deepEqual(result.selectedNodes, nodes.slice(0,2));
});
test('bulk off clears every registered tree layer, without mutating input', async () => {
const { getBulkLayerSelection } = await import(moduleUrl.href);
const nodes = [{id:'a',data:{exprsYn:'Y'}},{id:'hidden',data:{exprsYn:'N'}}];
assert.deepEqual(getBulkLayerSelection(nodes, false), {checkedIds:[],selectedNodes:[],uncheckedIds:['a','hidden']});
assert.equal(nodes[0].data.exprsYn, 'Y');
assert.deepEqual(getBulkLayerSelection([], true), {checkedIds:[],selectedNodes:[],uncheckedIds:[]});
});
-44
View File
@@ -1,44 +0,0 @@
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const test = require('node:test');
const ts = require('typescript');
const appRoot = path.resolve(__dirname, '..');
const source = fs.readFileSync(path.join(appRoot, 'src/shared/lib/row-number.ts'), 'utf8');
const compiled = ts.transpileModule(source, {
compilerOptions: { module: ts.ModuleKind.CommonJS },
}).outputText;
const moduleValue = { exports: {} };
vm.runInNewContext(compiled, { exports: moduleValue.exports, module: moduleValue });
const { getSnRowNumber } = moduleValue.exports;
test('non-paged rows start at one and do not depend on total count', () => {
assert.deepEqual([0, 1, 2].map(rowIndex => getSnRowNumber({total: 50, rowIndex})), [1, 2, 3]);
assert.equal(getSnRowNumber({total: 0, rowIndex: 0}), 1);
});
test('server pages continue numbering and preserve the final partial page', () => {
assert.equal(getSnRowNumber({total: 23, rowIndex: 0, pageIndex: 0, pageSize: 10}), 1);
assert.equal(getSnRowNumber({total: 23, rowIndex: 0, pageIndex: 1, pageSize: 10}), 11);
assert.equal(getSnRowNumber({total: 23, rowIndex: 2, pageIndex: 2, pageSize: 10}), 23);
});
test('page-size changes and refreshed result order use the new positions', () => {
assert.equal(getSnRowNumber({rowIndex: 0, pageIndex: 1, pageSize: 20}), 21);
assert.equal(getSnRowNumber({rowIndex: 0, pageIndex: 0, pageSize: 20}), 1);
});
function sourceFiles(directory) {
return fs.readdirSync(directory, {withFileTypes: true}).flatMap(entry => {
const full = path.join(directory, entry.name);
return entry.isDirectory() ? sourceFiles(full) : /\.tsx?$/.test(entry.name) ? [full] : [];
});
}
test('SN does not use the shared descending helper or legacy descending row formulas', () => {
for (const file of [...sourceFiles(path.join(appRoot, 'src')), ...sourceFiles(path.join(appRoot, 'app'))]) {
const text = fs.readFileSync(file, 'utf8');
assert.doesNotMatch(text, /getStandardRowNumber/, file);
assert.doesNotMatch(text, /(?:rowNumber|rowNo|no)\s*:\s*(?:[\w.]+\s*-\s*(?:index|detailIndex)|total\w*\s*-)/, file);
assert.doesNotMatch(text, /total(?:Elements|Count)?\s*-\s*\(\(?[^;{}\n]*(?:index|sourceIndex)/, file);
}
});