Skip to content

Commit

Permalink
Merge pull request #8 from conlacda/fix_issue_4_5_7
Browse files Browse the repository at this point in the history
fix #4 #5 #7
  • Loading branch information
conlacda authored Dec 7, 2024
2 parents 191c119 + 1398fd8 commit 1ceef60
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -1,68 +1,107 @@
import {test, expect} from '../fixtures';
import {Page, Locator, Download} from "@playwright/test";
import { test, expect } from '../fixtures';
import { Page, Locator, Download } from "@playwright/test";
import * as fs from 'fs';

require('dotenv').config({path: './.env'});
require('dotenv').config({ path: './.env' });

// chromium.launch PersistentContext prevents you from using multiple pages, so do not use parallel mode.
// The parallel mode opens each tab for each test, and the context will be closed after one test.
test.describe.configure({mode: 'serial'});
test.describe.configure({ mode: 'serial' });

let page: Page;
const EXTENDED_PROBLEM_SUBMISSION_URL: string = 'https://atcoder.jp/contests/abc298/submissions/60492340';
const CE_SUBMISSION_URL = 'https://atcoder.jp/contests/abc298/submissions/60492314';
const MAPPING_PROBLEM_SUBMISSION_URL = 'https://atcoder.jp/contests/abc044/submissions/60493350';

test.beforeAll(async ({context}) => {
test.beforeAll(async ({ context }) => {
page = await context.newPage();
const SUBMISSION_URL: string = 'https://atcoder.jp/contests/abc347/submissions/51807898';
await page.goto(SUBMISSION_URL);
});

test.afterAll(async () => {
await page.close();
});

test.describe('Extension functions correctly on the submission details page', () => {
test.describe('Copy button', () => {
test.describe('Copy button - extended problem', () => {
test('Copy input', async () => {
await page.goto(EXTENDED_PROBLEM_SUBMISSION_URL);
const copyButton: Locator = page.locator('#copy-in-0');
const copiedButton: Locator = page.locator('#copied-in-0');
await copyButton.dispatchEvent('click');
await expect(copiedButton).toBeVisible();
expect(await page.evaluate(() => navigator.clipboard.readText())).toContain("yay");
const copiedText = await page.evaluate(() => navigator.clipboard.readText());
const onlyNumberText = copiedText.replace(/\D/g, '');
expect(onlyNumberText).toBe('5344525153411253');
});
test('Copy output', async () => {
test('Copy input - mapping problem', async () => {
await page.goto(MAPPING_PROBLEM_SUBMISSION_URL);
const copyButton: Locator = page.locator('#copy-in-0');
const copiedButton: Locator = page.locator('#copied-in-0');
await copyButton.dispatchEvent('click');
await expect(copiedButton).toBeVisible();
const copiedText = await page.evaluate(() => navigator.clipboard.readText());
const onlyNumberText = copiedText.replace(/\D/g, '');
expect(onlyNumberText).toBe('53100009000');
});
test('Copy output - extended problem', async () => {
await page.goto(EXTENDED_PROBLEM_SUBMISSION_URL);
const copyButton: Locator = page.locator('#copy-out-0');
const copiedButton: Locator = page.locator('#copied-out-0');
await copyButton.dispatchEvent('click');
await expect(copiedButton).toBeVisible();
expect(await page.evaluate(() => navigator.clipboard.readText())).toContain("5");
const copiedText = await page.evaluate(() => navigator.clipboard.readText());
const onlyNumberText = copiedText.replace(/\D/g, '');
expect(onlyNumberText).toContain('463');
});
test('Copy output - mapping problem', async () => {
await page.goto(MAPPING_PROBLEM_SUBMISSION_URL);
const copyButton: Locator = page.locator('#copy-out-0');
const copiedButton: Locator = page.locator('#copied-out-0');
await copyButton.dispatchEvent('click');
await expect(copiedButton).toBeVisible();
const copiedText = await page.evaluate(() => navigator.clipboard.readText());
const onlyNumberText = copiedText.replace(/\D/g, '');
expect(onlyNumberText).toContain('48000');
});
});

test.describe('Download button', () => {
test('Download input', async () => {
await page.goto(EXTENDED_PROBLEM_SUBMISSION_URL);
const downloadButton: Locator = page.locator('#download-in-0');
const downloadPromise = page.waitForEvent('download');
await downloadButton.click();
const download: Download = await downloadPromise;
expect(download.suggestedFilename()).toBe("in-00_sample_01.txt");
expect(download.suggestedFilename()).toBe("in-sample00.txt");
await download.saveAs(download.suggestedFilename());
const contents = await fs.promises.readFile(await download.path(), 'utf-8');
expect(contents).toContain("yay");
const onlyNumberText = contents.replace(/\D/g, '');
expect(onlyNumberText).toContain('5344525153411253');
});
test('Download output', async () => {
await page.goto(EXTENDED_PROBLEM_SUBMISSION_URL);
const downloadButton: Locator = page.locator('#download-out-0');
const downloadPromise = page.waitForEvent('download');
await downloadButton.click();
const download: Download = await downloadPromise;
expect(download.suggestedFilename()).toBe("out-00_sample_01.txt");
expect(download.suggestedFilename()).toBe("out-sample00.txt");
await download.saveAs(download.suggestedFilename());
const contents = await fs.promises.readFile(await download.path(), 'utf-8');
expect(contents).toContain("5");
const onlyNumberText = contents.replace(/\D/g, '');
expect(onlyNumberText).toContain('463');
});
})

test('Debug button', async () => {
await page.goto(EXTENDED_PROBLEM_SUBMISSION_URL);
const debugButton: Locator = page.locator('#debug-0');
await debugButton.click();
await page.waitForURL('https://atcoder.jp/contests/abc347/custom_test?submissionId=51807898&testcase=00_sample_01&problem=B');
await page.waitForURL('https://atcoder.jp/contests/abc298/custom_test?submissionId=60492340&testcase=sample00&problem=Ex');
});

test('Do not show the debug button in a table that is not the result table', async () => {
await page.goto(CE_SUBMISSION_URL);
const debugButton: Locator = page.locator('th:has-text("Debug")');
await expect(debugButton).toHaveCount(0);
});
});
35 changes: 32 additions & 3 deletions browser-extension/chrome-extension-test/tests/testcase.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,21 @@ test.describe('Test cases should be added as the user settings', () => {

test.describe('Test download test cases', () => {
test('Download test cases button should be displayed', async () => {
// Normal problem
await page.goto('https://atcoder.jp/contests/agc062/tasks/agc062_b');
await expect(page.getByText('Download all test cases (43KB)')).toBeVisible();

await page.goto('https://atcoder.jp/contests/abc350/tasks/abc350_g');
await expect(page.getByText('Download all test cases (249.5MB)')).toBeVisible();
// Extended problem. Its url is abc256_h but its name starts with "Ex - "
await page.goto('https://atcoder.jp/contests/abc256/tasks/abc256_h');
await expect(page.getByText('Download all test cases (109.5MB)')).toBeVisible();

// Problem that has test case mapping to ARC contest
// Ex: test case of abc044_a is mapping to arc060_a
await page.goto('https://atcoder.jp/contests/abc044/tasks/abc044_a');
await expect(page.getByText('Download all test cases (242B)')).toBeVisible();
});

test('Download test cases as a zip file', async () => {
test('Download test cases as a zip file - normal problem', async () => {
await page.goto('https://atcoder.jp/contests/abc346/tasks/abc346_c');
await expect(page.getByText('Download all test cases (31.5MB)')).toBeVisible();
const downloadButton: Locator = page.locator('#dltc');
Expand All @@ -79,6 +86,28 @@ test.describe('Test download test cases', () => {
await download.saveAs(download.suggestedFilename());
// TODO: It should be better to test unzip and then check the folder structure and file contents.
});

test('Download test cases as a zip file - extended problem', async () => {
await page.goto('https://atcoder.jp/contests/abc256/tasks/abc256_h');
const downloadButton: Locator = page.locator('#dltc');
const downloadPromise = page.waitForEvent('download');
await downloadButton.click();
const download: Download = await downloadPromise;
expect(download.suggestedFilename()).toBe("abc256-Ex.zip");
await download.saveAs(download.suggestedFilename());
// TODO: It should be better to test unzip and then check the folder structure and file contents.
});

test('Download test cases as a zip file - mapping problem', async () => {
await page.goto('https://atcoder.jp/contests/abc044/tasks/abc044_a');
const downloadButton: Locator = page.locator('#dltc');
const downloadPromise = page.waitForEvent('download');
await downloadButton.click();
const download: Download = await downloadPromise;
expect(download.suggestedFilename()).toBe("arc060-A.zip");
await download.saveAs(download.suggestedFilename());
// TODO: It should be better to test unzip and then check the folder structure and file contents.
});
});

test.describe('A dialog should be displayed if the test cases size is too big', () => {
Expand Down
2 changes: 1 addition & 1 deletion browser-extension/chrome-extension/firefox_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "Atcoder Companion",
"description": "Enhance your experience by providing some useful features designed to streamline your workflow and boost your productivity.",
"version": "0.2.4",
"version": "0.2.5",
"action": {
"default_popup": "index.html",
"default_icon": {
Expand Down
9 changes: 7 additions & 2 deletions browser-extension/chrome-extension/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "Atcoder Companion",
"description": "Enhance your experience by providing some useful features designed to streamline your workflow and boost your productivity.",
"version": "0.2.4",
"version": "0.2.5",
"action": {
"default_popup": "index.html",
"default_icon": {
Expand Down Expand Up @@ -41,10 +41,14 @@
},
{
"js": [
"scripts/shared/testcase_mapping.js",
"scripts/testcase/main.js"
],
"matches": [
"*://atcoder.jp/contests/*/tasks/a*"
"*://atcoder.jp/contests/*/tasks/*"
],
"exclude_matches": [
"*://atcoder.jp/contests/*/tasks/"
]
},
{
Expand All @@ -60,6 +64,7 @@
},
{
"js": [
"scripts/shared/testcase_mapping.js",
"scripts/submission-details/problem-info.js",
"scripts/submission-details/testcase-downloader.js",
"scripts/submission-details/dom.js",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Source of this mapping is from https://kenkoooo.com/atcoder/resources/problems.json
* Check item['id'] and item['contest_id'] + "_" + item['problem_index']
* If they are different, we need to map them.
*/
const mappingForTestCase = {
"abc044": "arc060",
"abc045": "arc061",
"abc046": "arc062",
"abc047": "arc063",
"abc048": "arc064",
"abc049": "arc065",
"abc050": "arc066",
"abc052": "arc067",
"abc053": "arc068",
"abc055": "arc069",
"abc056": "arc070",
"abc058": "arc071",
"abc059": "arc072",
"abc060": "arc073",
"abc062": "arc074",
"abc063": "arc075",
"abc065": "arc076",
"abc066": "arc077",
"abc067": "arc078",
"abc068": "arc079",
"abc069": "arc080",
"abc071": "arc081",
"abc072": "arc082",
"abc074": "arc083",
"abc077": "arc084",
"abc078": "arc085",
"abc081": "arc086",
"abc082": "arc087",
"abc083": "arc088",
"abc086": "arc089",
"abc087": "arc090",
"abc090": "arc091",
"abc091": "arc092",
"abc092": "arc093",
"abc093": "arc094",
"abc094": "arc095",
"abc095": "arc096",
"abc097": "arc097",
"abc098": "arc098",
"abc101": "arc099",
"abc102": "arc100",
"abc107": "arc101",
"abc108": "arc102",
"abc111": "arc103",
}

10 changes: 4 additions & 6 deletions browser-extension/chrome-extension/scripts/shared/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,16 @@ const USER_SETTING_KEY = "user_settings";

/**
* Retrieve information about the current problem from the URL path.
* @returns {Array<string>} An array containing contest name and problem code extracted from the URL path.
* - The first element is the contest name.
* - The second element is the problem code in uppercase.
* @returns {{string, string}} An object containing contest name and problem code extracted from the URL path.
*/
const getProblemInfo = () => {
const curPath = window.location.pathname;
const regex = /contests\/.*\/tasks\/(.*)_(.*)/;
const match = regex.exec(curPath);
const contest = match[1];
const title = document.querySelector('span.h2').innerText;
const problem = (title.length > 0) ? title[0]: match[2].toUpperCase();
return [contest, problem];
const problemID = title.includes('-') ? title.split('-')[0].trim() : match[2].toUpperCase();
return {contest, problemID};
}

/**
Expand Down Expand Up @@ -51,7 +49,7 @@ const copyToClipboard = async (clipboard) => {

// save file content to local
const saveToLocal = async (content, fileName = "testcase.txt") => {
const blob = new Blob([content], {type: 'text/plain'});
const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
const testcaseList = await fetchTestCasesList(getContestName(), getProblemID());
if (testcaseList.length > 0) {
const resultTable = $('.table:last');
addInputColumnToResultTable(resultTable);
addOutputColumnToResultTable(resultTable);
addDebugColumnToResultTable(resultTable);
if (resultTable.text().toLowerCase().includes('case name')) {
addInputColumnToResultTable(resultTable);
addOutputColumnToResultTable(resultTable);
addDebugColumnToResultTable(resultTable);
}
}
})();
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ const curPath = window.location.pathname;
const getContestName = () => {
const regex = /contests\/(.*)\/submissions/gm;
const match = regex.exec(curPath);
return match[1];
// In some old contests, ABC & ARC contests share a part of problems.
// To download the test cases, we need to convert it to the correct contest that has uploaded test cases.
const contest = match[1];
return mappingForTestCase[contest] ?? contest;
};

/**
Expand All @@ -21,8 +24,9 @@ const getProblemID = () => {
return document.querySelector('.table')
.querySelectorAll('tr')[1]
.querySelector('a')
.innerText[0]
.toUpperCase();
.innerText
.split('-')[0]
.trim();
};

/**
Expand Down
11 changes: 6 additions & 5 deletions browser-extension/chrome-extension/scripts/testcase/main.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
const [contest, problem] = getProblemInfo();
let {contest, problemID} = getProblemInfo();
contest = mappingForTestCase[contest] ?? contest;
const copyButton = $('span[data-toggle="tooltip"]:visible').first();

(async () => {
// Add download all test cases button
const allTestCasesSz = await sizeOfAllTestCases(contest, problem);
const allTestCasesSz = await sizeOfAllTestCases(contest, problemID);
if (allTestCasesSz === 0)
return;

Expand All @@ -12,15 +13,15 @@ const copyButton = $('span[data-toggle="tooltip"]:visible').first();
downloadButton.onclick = async () => {
downloadButton.disabled = true;
downloadButton.textContent += '\u{231B}';
await downloadAllTestCases(contest, problem);
await downloadAllTestCases(contest, problemID);
setTimeout(() => {
downloadButton.textContent = downloadButton.textContent.replace('⌛', '');
downloadButton.disabled = false;
}, 2000);
}

// confirm before loading all of test cases if they are too large.
const testCasesSzInBytes = await sizeOfTestCasesByUserSettings(contest, problem);
const testCasesSzInBytes = await sizeOfTestCasesByUserSettings(contest, problemID);
if (testCasesSzInBytes > LARGE_SIZE_IN_BYTES) {
if (!window.confirm(`The size of all the test cases is too large (${humanReadable(testCasesSzInBytes)}). Loading all of them might make your browser crash. Still load?`)) {
return;
Expand All @@ -29,7 +30,7 @@ const copyButton = $('span[data-toggle="tooltip"]:visible').first();

// Add testcase to the test case section
const taskStatement = document.getElementById('task-statement');
const testcases = await fetchTestCasesByUserSettings(contest, problem);
const testcases = await fetchTestCasesByUserSettings(contest, problemID);
testcases.forEach((tc) => {
if (tc.isSample())
return;
Expand Down

0 comments on commit 1ceef60

Please sign in to comment.