chore: strip data-testid on prod

This commit is contained in:
nathan
2025-09-05 14:15:32 +08:00
parent 10b3864805
commit 5e1d2e5345
3 changed files with 111 additions and 84 deletions

View File

@@ -1,7 +1,7 @@
import { v4 as uuidv4 } from 'uuid';
import { AuthTestUtils } from '../../support/auth-utils';
import { TestTool } from '../../support/page-utils';
import { PageSelectors, ViewActionSelectors, SpaceSelectors, waitForReactUpdate } from '../../support/selectors';
import { PageSelectors, waitForReactUpdate } from '../../support/selectors';
describe('More Page Actions', () => {
const APPFLOWY_BASE_URL = Cypress.env('APPFLOWY_BASE_URL');
@@ -136,62 +136,4 @@ describe('More Page Actions', () => {
cy.task('log', 'Page successfully duplicated');
});
// it('should open Change Icon popover from More actions', () => {
// });
// it('should remove icon via Change Icon popover', () => {
// TestTool.morePageActionsChangeIcon();
// cy.get('[role="dialog"]').within(() => {
// cy.contains('button', 'Remove').click();
// });
// TestTool.getModal().should('not.exist');
// cy.get('.view-icon').should('not.exist');
// });
// it('should upload a custom icon via Change Icon popover', () => {
// TestTool.morePageActionsChangeIcon();
// cy.get('[role="dialog"]').within(() => {
// cy.get('input[type="file"]').attachFile('test-icon.png');
// });
// TestTool.getModal().should('not.exist');
// cy.get('.view-icon').should('exist');
// });
// it('should open page in a new tab from More actions', () => {
// cy.window().then((win) => {
// cy.stub(win, 'open').as('open');
// });
// TestTool.morePageActionsOpenNewTab();
// cy.get('@open').should('be.called');
// });
// it('should duplicate page from More actions and show success toast', () => {
// TestTool.morePageActionsDuplicate();
// cy.wait(2000);
// TestTool.getPageByName('Get started').should('have.length', 2);
// });
// it('should move page to another space from More actions', () => {
// TestTool.morePageActionsMoveTo();
// cy.get('[role="dialog"]').within(() => {
// TestTool.getSpaceItems().first().click();
// });
// cy.wait(2000);
// TestTool.getPageByName('Get started').should('be.visible');
// });
// it('should delete page from More actions and confirm deletion', () => {
// TestTool.morePageActionsDelete();
// TestTool.confirmPageDeletion();
// cy.wait(2000);
// TestTool.getPageByName('Get started').should('not.exist');
// });
});

View File

@@ -0,0 +1,82 @@
import { Plugin } from 'vite';
/**
* Vite plugin to strip data-testid attributes from production builds
* This reduces bundle size and removes test-specific attributes from production code
*/
export function stripTestIdPlugin(): Plugin {
return {
name: 'strip-test-id',
apply: 'build', // Only apply during build, not dev
transform(code: string, id: string) {
// Skip node_modules to avoid transforming external libraries
if (id.includes('node_modules')) {
return null;
}
// Only process .tsx and .jsx files from our source
if (!id.match(/src.*\.(tsx|jsx)$/)) {
return null;
}
// Only strip in production builds
if (process.env.NODE_ENV !== 'production') {
return null;
}
let transformedCode = code;
let hasChanges = false;
try {
// Pattern 1: Simple string attributes: data-testid="value" or data-testid='value'
// This is the safest pattern to remove
const simpleStringPattern = /\s+data-testid\s*=\s*["'][^"']*["']/g;
const matches = transformedCode.match(simpleStringPattern);
if (matches && matches.length > 0) {
console.log(`Stripping ${matches.length} data-testid attributes from ${id}`);
transformedCode = transformedCode.replace(simpleStringPattern, '');
hasChanges = true;
}
// Pattern 2: Simple expressions without nested braces: data-testid={variable}
const simpleExpressionPattern = /\s+data-testid\s*=\s*\{[^{}]+\}/g;
const exprMatches = transformedCode.match(simpleExpressionPattern);
if (exprMatches && exprMatches.length > 0) {
console.log(`Stripping ${exprMatches.length} data-testid expressions from ${id}`);
transformedCode = transformedCode.replace(simpleExpressionPattern, '');
hasChanges = true;
}
// Pattern 3: Template literals: data-testid={`value-${id}`}
const templatePattern = /\s+data-testid\s*=\s*\{`[^`]*`\}/g;
if (templatePattern.test(transformedCode)) {
transformedCode = transformedCode.replace(templatePattern, '');
hasChanges = true;
}
if (hasChanges) {
// Quick validation: check for obvious syntax errors
// Count opening and closing braces to ensure we didn't break anything
const openBraces = (transformedCode.match(/\{/g) || []).length;
const closeBraces = (transformedCode.match(/\}/g) || []).length;
if (openBraces !== closeBraces) {
console.warn(`Warning: Brace mismatch after transformation in ${id}. Skipping transformation.`);
return null;
}
return {
code: transformedCode,
map: null,
};
}
} catch (error) {
console.error(`Error processing ${id}:`, error);
// Return null to skip transformation on error
return null;
}
return null;
},
};
}

View File

@@ -7,6 +7,7 @@ import { createHtmlPlugin } from 'vite-plugin-html';
import istanbul from 'vite-plugin-istanbul';
import svgr from 'vite-plugin-svgr';
import { totalBundleSize } from 'vite-plugin-total-bundle-size';
import { stripTestIdPlugin } from './vite-plugin-strip-testid';
const resourcesPath = path.resolve(__dirname, '../resources');
const isDev = process.env.NODE_ENV === 'development';
@@ -17,6 +18,8 @@ const isTest = process.env.NODE_ENV === 'test' || process.env.COVERAGE === 'true
export default defineConfig({
plugins: [
react(),
// Strip data-testid attributes in production builds
isProd ? stripTestIdPlugin() : undefined,
createHtmlPlugin({
inject: {
data: {