diff --git a/.github/workflows/deploy-develop.yml b/.github/workflows/deploy-develop.yml index 8546e26..2b84207 100644 --- a/.github/workflows/deploy-develop.yml +++ b/.github/workflows/deploy-develop.yml @@ -94,35 +94,7 @@ jobs: - name: Save develop build run: mv dist develop-dist - # Second: Build main branch content for the root - - name: Checkout and build main branch with test site link - run: | - # Save current develop state - git stash push -m "Save develop changes" || echo "No changes to stash" - - # Checkout main branch with full history and ensure tags are available - git checkout main - git pull origin main || echo "No changes to pull" - - # Ensure all tags are fetched for version calculation - git fetch --tags || echo "Tags already available" - - # Build main branch content with proper main branch GitVersion - npm run build - - # Save main build - mv dist main-dist - - # Return to develop branch and restore state - git checkout develop - if git stash list | grep -q "Save develop changes"; then - git stash pop - fi - env: - # Pass test site info for footer link - TEST_SITE_PATH: ${{ steps.pr-info.outputs.deploy_path }} - - # Get GitVersion for main branch after checking it out + # Get GitVersion for main branch - name: Get main branch GitVersion id: gitversion-main run: | @@ -153,8 +125,8 @@ jobs: git stash pop fi - # Rebuild main with proper GitVersion values - - name: Rebuild main branch with correct GitVersion + # Build main branch with proper GitVersion values + - name: Build main branch with correct GitVersion run: | # Save current develop state git stash push -m "Save develop changes" || echo "No changes to stash" diff --git a/gitversion.yml b/gitversion.yml index 7f132b2..6a508e2 100644 --- a/gitversion.yml +++ b/gitversion.yml @@ -7,14 +7,14 @@ commit-message-incrementing: Enabled branches: main: regex: ^main$ - mode: ContinuousDelivery + mode: ContinuousDeployment label: '' increment: Patch develop: regex: ^develop$ mode: ContinuousDeployment label: alpha - increment: None + increment: Patch release: regex: ^releases?[/-] mode: ContinuousDelivery diff --git a/src/scripts/index.ts b/src/scripts/index.ts index eb399a8..6bf36e7 100644 --- a/src/scripts/index.ts +++ b/src/scripts/index.ts @@ -1265,17 +1265,38 @@ const processedTimezoneCache = new Map(); // Cache key for localStorage const TIMEZONE_CACHE_KEY = 'everytimezone_processed_timezones'; +/** + * Type guard to check if an object is a valid TimeZone + */ +function isValidTimeZone(obj: unknown): obj is TimeZone { + return ( + typeof obj === 'object' && + obj !== null && + typeof (obj as TimeZone).name === 'string' && + typeof (obj as TimeZone).offset === 'number' && + typeof (obj as TimeZone).displayName === 'string' && + typeof (obj as TimeZone).iana === 'string' && + typeof (obj as TimeZone).cityName === 'string' && + typeof (obj as TimeZone).abbreviation === 'string' + // Note: daylight, isCustom, isOffCycle, coordinates are optional properties + ); +} + /** * Type guard to check if an object is a valid ProcessedTimezoneData */ function isProcessedTimezoneData(obj: unknown): obj is ProcessedTimezoneData { + const candidate = obj as ProcessedTimezoneData; return ( typeof obj === 'object' && obj !== null && - Array.isArray((obj as ProcessedTimezoneData).juneTimeZones) && - Array.isArray((obj as ProcessedTimezoneData).decemberTimeZones) && - typeof (obj as ProcessedTimezoneData).userTimezone === 'string' && - typeof (obj as ProcessedTimezoneData).currentYear === 'number' + Array.isArray(candidate.juneTimeZones) && + Array.isArray(candidate.decemberTimeZones) && + typeof candidate.userTimezone === 'string' && + typeof candidate.currentYear === 'number' && + // Validate that array contents are valid TimeZone objects to prevent runtime errors + candidate.juneTimeZones.every(isValidTimeZone) && + candidate.decemberTimeZones.every(isValidTimeZone) ); } diff --git a/test/accessibility-comprehensive.test.ts b/test/accessibility-comprehensive.test.ts index 5bf0d00..16763d8 100644 --- a/test/accessibility-comprehensive.test.ts +++ b/test/accessibility-comprehensive.test.ts @@ -293,15 +293,15 @@ describe('WCAG AAA Accessibility Standards', () => { it('should meet WCAG AAA color contrast requirements (7:1 for normal text)', () => { // Test all text elements for complete accessibility coverage const textElements = document.querySelectorAll('*'); - let contrastIssues: string[] = []; - - textElements.forEach((element, index) => { + + // Use functional approach for better performance instead of repeatedly calling push() + const contrastIssues = Array.from(textElements).flatMap((element, index) => { const styles = window.getComputedStyle(element); const textColor = styles.color; const backgroundColor = getComputedBackgroundColor(element); // Test all elements including hidden ones for screen reader compatibility - if (!element.textContent?.trim()) return; + if (!element.textContent?.trim()) return []; if (textColor && backgroundColor) { const textRGB = parseColor(textColor); @@ -311,12 +311,11 @@ describe('WCAG AAA Accessibility Standards', () => { const contrastRatio = getContrastRatio(textRGB, bgRGB); // WCAG AAA requires 7:1 for normal text (not 4.5:1 which is AA) if (contrastRatio < 7.0) { - contrastIssues.push( - `${element.tagName}[${index}] "${element.textContent?.trim().substring(0, 20)}..." contrast ratio ${contrastRatio.toFixed(2)}:1 is below WCAG AAA standard (7:1)` - ); + return [`${element.tagName}[${index}] "${element.textContent?.trim().substring(0, 20)}..." contrast ratio ${contrastRatio.toFixed(2)}:1 is below WCAG AAA standard (7:1)`]; } } } + return []; }); expect(contrastIssues.length, `Contrast issues found at ${size.name}: ${contrastIssues.join(', ')}`).toBe(0); @@ -325,15 +324,15 @@ describe('WCAG AAA Accessibility Standards', () => { it('should meet WCAG AAA large text contrast requirements (4.5:1)', () => { // Test all elements for complete accessibility coverage const allElements = document.querySelectorAll('*'); - let contrastIssues: string[] = []; - - allElements.forEach((element, index) => { + + // Use functional approach for better performance instead of repeatedly calling push() + const contrastIssues = Array.from(allElements).flatMap((element, index) => { const styles = window.getComputedStyle(element); const fontSize = parseFloat(styles.fontSize); const fontWeight = styles.fontWeight; // Test all elements including hidden ones for screen reader compatibility - if (!element.textContent?.trim()) return; + if (!element.textContent?.trim()) return []; // Large text is 18pt+ (24px+) or 14pt+ (18.5px+) bold according to WCAG const isLargeText = fontSize >= 24 || (fontSize >= 18.5 && (fontWeight === 'bold' || parseInt(fontWeight) >= 700)); @@ -350,13 +349,12 @@ describe('WCAG AAA Accessibility Standards', () => { const contrastRatio = getContrastRatio(textRGB, bgRGB); // WCAG AAA requires 4.5:1 for large text if (contrastRatio < 4.5) { - contrastIssues.push( - `Large text ${element.tagName}[${index}] "${element.textContent?.trim().substring(0, 20)}..." contrast ratio ${contrastRatio.toFixed(2)}:1 is below WCAG AAA standard (4.5:1)` - ); + return [`Large text ${element.tagName}[${index}] "${element.textContent?.trim().substring(0, 20)}..." contrast ratio ${contrastRatio.toFixed(2)}:1 is below WCAG AAA standard (4.5:1)`]; } } } } + return []; }); expect(contrastIssues.length, `Large text contrast issues at ${size.name}: ${contrastIssues.join(', ')}`).toBe(0);