Skip to content

Commit

Permalink
Merge pull request #36 from koudaiii/koudaiii/ago
Browse files Browse the repository at this point in the history
Feature: Display Time Elapsed Since Last Update (e.g., "xx days ago")
  • Loading branch information
koudaiii authored Sep 18, 2024
2 parents 5eb6d1a + 584d437 commit daabdb6
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 5 deletions.
51 changes: 50 additions & 1 deletion src/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@ const languageLabels = {
// Add more language labels as needed. For example: 'fr-fr': 'Dernière mise à jour le',
};

// timeAgoLabels is a dictionary that maps message from language codes to the corresponding language
// Default timeAgoLabels is 'years ago', 'days ago', 'hours ago', 'minutes ago', 'just now'
// Add more timeAgoLabels as needed
const timeAgoLabels = {
'ja-jp': {
years: '年前に更新',
days: '日前に更新',
hours: '時間前に更新',
minutes: '分前に更新',
justNow: '今更新されたばかり',
},
// Add more language labels as needed. For example:
// 'fr-fr': { years: 'il y a ans', days: 'il y a jours', hours: 'il y a heures', minutes: 'il y a minutes', justNow: 'à l\'instant' },
};

(async () => {
// Get current URL
const currentUrl = window.location.href;
Expand Down Expand Up @@ -46,6 +61,38 @@ const languageLabels = {
const englishDate = new Date(englishDateStr);

// Add update info to current page
// Calculate the difference in time between the current date and the English update date
const currentDate = new Date();
const timeDifference = currentDate - englishDate;

// Create a new paragraph element to display the update information
let timeAgo;
const years = Math.floor(timeDifference / (1000 * 60 * 60 * 24 * 365));
const days = Math.floor((timeDifference % (1000 * 60 * 60 * 24 * 365)) / (1000 * 60 * 60 * 24));
const hours = Math.floor((timeDifference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60));

const labels = timeAgoLabels[currentLang] || {
years: 'years ago',
days: 'days ago',
hours: 'hours ago',
minutes: 'minutes ago',
justNow: 'just now'
};

if (years > 0) {
timeAgo = ` ${years} ${labels.years}`;
} else if (days > 0) {
timeAgo = ` ${days} ${labels.days}`;
} else if (hours > 0) {
timeAgo = ` ${hours} ${labels.hours}`;
} else if (minutes > 0) {
timeAgo = ` ${minutes} ${labels.minutes}`;
} else {
timeAgo = labels.justNow;
}
let timeAgoStr = ` (${timeAgo})`;

const updateInfo = document.createElement("p");
dataArticleDateElement.parentElement.appendChild(updateInfo);

Expand All @@ -64,6 +111,8 @@ const languageLabels = {

console.log("English date:", englishDate);
console.log("Article date:", articleDate);
console.log("timeAgoStr:", timeAgoStr);

// Compare English date and Article date
if (englishDate > articleDate || debug === "true") {
// Display alert if English page is updated
Expand All @@ -82,7 +131,7 @@ const languageLabels = {
const languageLabel = languageLabels[currentLang] || 'last updated on';

// Display update info
updateInfo.innerHTML = informationIcon + `${languageLabel}: <a href="${englishUrl}" target="_blank" class="${textColorClass}">${englishDate.toLocaleDateString(currentLang)}</a>`;
updateInfo.innerHTML = informationIcon + `${languageLabel}: <a href="${englishUrl}" target="_blank" class="${textColorClass}">${englishDate.toLocaleDateString(currentLang)}${timeAgoStr}</a>`;
}
updateClass();
const observer = new MutationObserver(updateClass);
Expand Down
21 changes: 17 additions & 4 deletions tests/e2e/content.e2e.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,15 @@ describe('learn.microsoft.com Update Checker E2E Test', () => {
];

for (const timezone of timezones) {
console.log("timezone:", timezone);
testCases.forEach((testCase) => {
console.log("testCase:", testCase);
test(`should display English update date on ${testCase.url} with ${testCase.themeColor} theme in ${testCase.prefersColorScheme} mode`, async () => {
await page.goto(testCase.url);
await page.emulateMediaFeatures([
{ name: "prefers-color-scheme", value: testCase.prefersColorScheme },
]);
await page.emulateTimezone(timezone);
console.log("timezone:", timezone);

// Click the button to set the theme to the desired color scheme
await page.evaluate((themeColor) => {
Expand All @@ -99,7 +100,6 @@ describe('learn.microsoft.com Update Checker E2E Test', () => {
}, testCase.themeColor);
await page.waitForSelector('button[aria-pressed="true"]');


// Wait for the time element with the 'data-article-date' attribute to be added
await page.waitForSelector('time[data-article-date]');

Expand All @@ -120,6 +120,21 @@ describe('learn.microsoft.com Update Checker E2E Test', () => {
return textElement !== null;
}, testCase.textElementSelector);
expect(hasTextColorClass).toBe(true);

// get text content. e.g. '2023/12/21 英語版の更新日: 2023/12/21 (271日前に更新)'
const textContentStr = await page.evaluate(() => {
const visibilityHiddenVisualDiffElement = document.querySelector('li.visibility-hidden-visual-diff');
return visibilityHiddenVisualDiffElement ? visibilityHiddenVisualDiffElement.innerText : null;
});
console.log("textContentStr:", textContentStr);

let defaultUpdatePattern = /(years|days|hours|minutes) ago|(just now)/;
if (testCase.url.includes('ja-jp')) {
defaultUpdatePattern = /(年|日|時間|分)前に更新|(今更新されたばかり)/;
}
let match = textContentStr.match(defaultUpdatePattern);
console.log("match:", match);
expect(!!match).toBe(true);
});

test(`should not run script on en-us pages with ${testCase.themeColor} theme in ${testCase.prefersColorScheme} mode`, async () => {
Expand All @@ -129,7 +144,6 @@ describe('learn.microsoft.com Update Checker E2E Test', () => {
{ name: "prefers-color-scheme", value: testCase.prefersColorScheme },
]);
await page.emulateTimezone(timezone);
console.log("timezone:", timezone);

// Click the button to set the theme to the desired color scheme
await page.evaluate((themeColor) => {
Expand All @@ -150,7 +164,6 @@ describe('learn.microsoft.com Update Checker E2E Test', () => {
test('should run script with jp-learn-microsoft-com-update-checker-debug flag', async () => {
await page.goto('https://learn.microsoft.com/ja-jp/azure/virtual-machines/overview?jp-learn-microsoft-com-update-checker-debug=true');
await page.emulateTimezone(timezone);
console.log("timezone:", timezone);

// Wait for the paragraph element with the 'alert' class to be added
await page.waitForSelector('p.alert');
Expand Down
81 changes: 81 additions & 0 deletions tests/unit/content.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,85 @@ describe('URL Check in Content Script', () => {
const currentUrl = 'https://learn.microsoft.com/en-us/azure/virtual-machines/overview';
expect(currentUrl.startsWith('https://learn.microsoft.com/ja-jp/')).toBe(false);
});
test('should match "年前に更新"', () => {
const textContent = '2 年前に更新';
const match = textContent.match(/(年|日|時間|分)前に更新|(今更新されたばかり)/);
expect(match).not.toBeNull();
expect(match[0]).toBe('年前に更新');
});

test('should match "日前に更新"', () => {
const textContent = '3 日前に更新';
const match = textContent.match(/(年|日|時間|分)前に更新|(今更新されたばかり)/);
expect(match).not.toBeNull();
expect(match[0]).toBe('日前に更新');
});

test('should match "時間前に更新"', () => {
const textContent = '5 時間前に更新';
const match = textContent.match(/(年|日|時間|分)前に更新|(今更新されたばかり)/);
expect(match).not.toBeNull();
expect(match[0]).toBe('時間前に更新');
});

test('should match "分前に更新"', () => {
const textContent = '10 分前に更新';
const match = textContent.match(/(年|日|時間|分)前に更新|(今更新されたばかり)/);
expect(match).not.toBeNull();
expect(match[0]).toBe('分前に更新');
});

test('should match "今更新されたばかり"', () => {
const textContent = '今更新されたばかり';
const match = textContent.match(/(年|日|時間|分)前に更新|(今更新されたばかり)/);
expect(match).not.toBeNull();
expect(match[0]).toBe('今更新されたばかり');
});

test('should not match unrelated text', () => {
const textContent = 'This is some unrelated text';
const match = textContent.match(/(年|日|時間|分)前に更新|(今更新されたばかり)/);
expect(match).toBeNull();
});

test('should match "years ago"', () => {
const textContent = '2 years ago';
const match = textContent.match(/(years|days|hours|minutes) ago|just now/);
expect(match).not.toBeNull();
expect(match[0]).toBe('years ago');
});

test('should match "days ago"', () => {
const textContent = '3 days ago';
const match = textContent.match(/(years|days|hours|minutes) ago|just now/);
expect(match).not.toBeNull();
expect(match[0]).toBe('days ago');
});

test('should match "hours ago"', () => {
const textContent = '5 hours ago';
const match = textContent.match(/(years|days|hours|minutes) ago|just now/);
expect(match).not.toBeNull();
expect(match[0]).toBe('hours ago');
});

test('should match "minutes ago"', () => {
const textContent = '10 minutes ago';
const match = textContent.match(/(years|days|hours|minutes) ago|just now/);
expect(match).not.toBeNull();
expect(match[0]).toBe('minutes ago');
});

test('should match "just now"', () => {
const textContent = 'just now';
const match = textContent.match(/(years|days|hours|minutes) ago|just now/);
expect(match).not.toBeNull();
expect(match[0]).toBe('just now');
});

test('should not match unrelated text', () => {
const textContent = 'This is some unrelated text';
const match = textContent.match(/(years|days|hours|minutes) ago|just now/);
expect(match).toBeNull();
});
});

0 comments on commit daabdb6

Please sign in to comment.