import { chromium } from 'playwright'; import { mkdirSync } from 'fs'; const screenshotDir = 'C:/tmp/playwright_screenshots'; mkdirSync(screenshotDir, { recursive: true }); const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ viewport: { width: 1400, height: 900 } }); const page = await context.newPage(); async function screenshot(name) { const path = `${screenshotDir}/${name}.png`; await page.screenshot({ path, fullPage: false }); console.log(` [Screenshot: ${name}.png]`); } // Login await page.goto('http://localhost:3000/fertilizer/new'); await page.waitForLoadState('networkidle'); if (page.url().includes('/login')) { await page.fill('#username', 'akira'); await page.fill('input[type="password"]', 'keina2025'); await page.click('button[type="submit"]'); await page.waitForNavigation({ timeout: 10000 }).catch(() => {}); await page.waitForLoadState('networkidle'); await page.goto('http://localhost:3000/fertilizer/new'); await page.waitForLoadState('networkidle'); } // Setup: Select にこまる, add グアノ, enter 3 and calculate const selects = await page.locator('select').all(); await selects[1].selectOption({ label: 'にこまる' }); await page.waitForTimeout(1000); await page.locator('button').filter({ hasText: '肥料を追加' }).first().click(); await page.waitForTimeout(500); await page.locator('text=グアノ').first().click(); await page.waitForTimeout(1000); await page.locator('input[placeholder="値"]').fill('3'); await page.locator('button').filter({ hasText: '計算' }).click(); await page.waitForTimeout(1500); // Verify state before ≈ click console.log('\n=== BEFORE clicking ≈ ==='); const inputsBefore = await page.locator('input[type="number"]').all(); console.log('Matrix cell values (before ≈):'); const valuesBefore = []; for (let i = 1; i < inputsBefore.length; i++) { const val = await inputsBefore[i].inputValue(); valuesBefore.push(val); console.log(` Cell[${i}]: ${val}`); } // Check the ≈ button const approxBtn = page.locator('button', { hasText: '≈' }); const approxBtnClass = await approxBtn.getAttribute('class'); console.log('\n≈ button class:', approxBtnClass); const approxBtnText = await approxBtn.textContent(); console.log('≈ button text:', approxBtnText?.trim()); await screenshot('step6_before_approx_click'); // Step 8: Click ≈ button console.log('\n=== Step 8: Click ≈ button ==='); await approxBtn.click(); await page.waitForTimeout(1500); await screenshot('step9_after_approx_click'); // Step 9: Check values after ≈ click console.log('\n=== Step 9: Check values AFTER clicking ≈ ==='); const inputsAfter = await page.locator('input[type="number"]').all(); console.log('Matrix cell values (after ≈):'); const valuesAfter = []; for (let i = 1; i < inputsAfter.length; i++) { const val = await inputsAfter[i].inputValue(); valuesAfter.push(val); console.log(` Cell[${i}]: ${val}`); } // Check for reference values (gray text) - might be in span or other elements // Look for elements that show original calc values as reference const refValueElements = await page.locator('[class*="gray"], [class*="text-gray"], [class*="ref"]').all(); console.log('\nLooking for reference value indicators...'); // Check all visible text in the table area const tableText = await page.locator('table, [role="table"], .table-auto, [class*="table"]').first().textContent().catch(() => null); if (tableText) { console.log('Table text:', tableText.replace(/\s+/g, ' ').substring(0, 500)); } else { // Try to get the section after the header const bodyText = await page.locator('body').textContent(); const afterHeader = bodyText?.match(/圃場名.*面積.*グアノ.{0,1000}/s)?.[0]; console.log('Matrix section:', afterHeader?.replace(/\s+/g, ' ').substring(0, 500)); } // Find the new button (should be ↩ now) const allButtonsAfter = await page.locator('button').all(); console.log('\nLooking for ↩ button:'); for (let i = 0; i < allButtonsAfter.length; i++) { const text = await allButtonsAfter[i].textContent(); const cls = await allButtonsAfter[i].getAttribute('class'); if (text && text.trim().length <= 3 && text.trim() !== '') { console.log(` Button[${i}]: text="${text?.trim()}", class="${cls?.substring(0, 100)}"`); } } // Step 10: Click ↩ button console.log('\n=== Step 10: Click ↩ button ==='); const restoreBtn = page.locator('button').filter({ hasText: '↩' }); const restoreBtnVisible = await restoreBtn.isVisible().catch(() => false); console.log('↩ button visible:', restoreBtnVisible); if (restoreBtnVisible) { const restoreBtnClass = await restoreBtn.getAttribute('class'); console.log('↩ button class:', restoreBtnClass); await restoreBtn.click(); await page.waitForTimeout(1500); await screenshot('step11_after_restore'); // Step 11: Check values restored console.log('\n=== Step 11: Check values AFTER clicking ↩ ==='); const inputsRestored = await page.locator('input[type="number"]').all(); console.log('Matrix cell values (after ↩):'); for (let i = 1; i < inputsRestored.length; i++) { const val = await inputsRestored[i].inputValue(); console.log(` Cell[${i}]: ${val}`); } // Check button is back to ≈ const approxBtnRestored = page.locator('button', { hasText: '≈' }); const approxVisible = await approxBtnRestored.isVisible().catch(() => false); const approxClass = await approxBtnRestored.getAttribute('class').catch(() => null); console.log('\n≈ button visible again:', approxVisible); console.log('≈ button class:', approxClass); } else { console.log('WARNING: ↩ button not found!'); // Print all button texts for debugging for (let i = 0; i < allButtonsAfter.length; i++) { const text = await allButtonsAfter[i].textContent(); console.log(` Button[${i}]: "${text?.trim()}"`); } } await browser.close();