The solution on Medium referenced in this question is out of date for Angular 8+.
Firstly, the angular-cli-builders
npm package is deprecated. You will need the version of @angular-builders/custom-webpack that is appropriate for your Angular version instead.
Then, if your builds are using ahead-of-time compilation - which most production configurations will - then it is very difficult to intercept and replace the data-cy
attributes within source .html files. The only way I could reliably get this to work was to examine the built .js output, and replace the attributes within there instead.
Here's the relevant parts of a working angular.json
file for Angular 9:
{
"projects": {
"datacy": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"sourceMap": true,
"namedChunks": true
},
"configurations": {
"production": {
"customWebpackConfig": {
"path": "./webpack-config.prod.ts",
"mergeStrategies": {
"module.rules": "prepend"
}
},
"optimization": true,
"aot": true,
...
webpack.config.prod.ts
import * as path from 'path';
import * as webpack from 'webpack';
export default {
module: {
rules: [{
test: /\.js$/,
use: [{ loader: path.resolve('./data-cy-loader.ts') }],
}],
},
} as webpack.Configuration;
data-cy-loader.ts
/*
HTML formatting looks like <element data-cy="value">
Inlined within a javascript string, it looks like <element data-cy=\"value\">
Compiled AOT, the element attributes are turned into an array:
[["class", "cssClassName"], ["data-cy", "value"], ...]
The data-cy attribute may appear at the start, middle, or end of this array.
*/
export default (source: string) => {
if (source.indexOf('data-cy') >= 0) {
return source.replace(/\["data-cy" ?,"([^"]*)"\],/g, '') // ["data-cy", "..."], variants
.replace(/, ?\["data-cy", ?"([^"]*)"\]/g, '') // , ["data-cy", "..."]] variant
.replace(/(\[")?data-cy(=|(",))\\?"([^"]*)"(\])?/g, ''); // [["data-cy","..."]] and html variants
}
return source;
};