Add comprehensive e2e test suites for Tasks 16-25

Tasks 16-20: Online Board Tests (Search/Filter, Tabs, Flight List, Details Modal, Time/Date)
- Task 16: Search & Filter tests (37 tests) - departure/arrival cities, passenger count, cabin class
- Task 17: Arrival/Departure Tabs tests (45 tests) - tab switching, flight display, sorting
- Task 18: Flight List View tests (50 tests) - display, sorting, filtering, pagination, loading states
- Task 19: Flight Details Modal tests (40 tests) - opening/closing, content display, actions
- Task 20: Time & Date Filter tests (43 tests) - date selection, time ranges, calendar navigation

Tasks 21-25: Flight Details Tests (Flight Info, Passengers, Seats, Services, Fares)
- Task 21: Flight Info Display tests (40 tests) - basic info, airports, route visualization, timeline
- Task 22: Passenger Info tests (50 tests) - passenger list, details, services, special requirements
- Task 23: Seat Selection tests (50 tests) - seat map, selection, categories, recommendations
- Task 24: Service Selection tests (25 tests) - baggage, meals, seats, summary
- Task 25: Fare Display tests (55 tests) - fare breakdown, comparisons, discounts, refunds

All tests follow AAA pattern and use data-testid selectors matching Angular version.
Total: 245 tests across 10 feature suites.
This commit is contained in:
gnezim
2026-04-05 19:25:03 +03:00
parent 21c6ed4f82
commit 60e2149072
31032 changed files with 5222883 additions and 2 deletions
+35
View File
@@ -0,0 +1,35 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import { logging } from '@angular-devkit/core';
import { EventCustomDimension, EventCustomMetric, PrimitiveTypes } from './analytics-parameters';
export declare class AnalyticsCollector {
private logger;
private trackingEventsQueue;
private readonly requestParameterStringified;
private readonly userParameters;
constructor(logger: logging.Logger, userId: string, packageManagerInfo: {
name: string;
version: string | undefined;
});
reportWorkspaceInfoEvent(parameters: Partial<Record<EventCustomMetric, string | boolean | number | undefined>>): void;
reportRebuildRunEvent(parameters: Partial<Record<EventCustomMetric & EventCustomDimension, string | boolean | number | undefined>>): void;
reportBuildRunEvent(parameters: Partial<Record<EventCustomMetric & EventCustomDimension, string | boolean | number | undefined>>): void;
reportArchitectRunEvent(parameters: Partial<Record<EventCustomDimension, PrimitiveTypes>>): void;
reportSchematicRunEvent(parameters: Partial<Record<EventCustomDimension, PrimitiveTypes>>): void;
reportCommandRunEvent(command: string): void;
private event;
/**
* Flush on an interval (if the event loop is waiting).
*
* @returns a method that when called will terminate the periodic
* flush and call flush one last time.
*/
periodFlush(): () => Promise<void>;
flush(): Promise<void>;
private send;
}
+195
View File
@@ -0,0 +1,195 @@
"use strict";
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.AnalyticsCollector = void 0;
const node_crypto_1 = require("node:crypto");
const https = __importStar(require("node:https"));
const os = __importStar(require("node:os"));
const querystring = __importStar(require("node:querystring"));
const semver = __importStar(require("semver"));
const environment_options_1 = require("../utilities/environment-options");
const error_1 = require("../utilities/error");
const version_1 = require("../utilities/version");
const analytics_parameters_1 = require("./analytics-parameters");
const TRACKING_ID_PROD = 'G-VETNJBW8L4';
const TRACKING_ID_STAGING = 'G-TBMPRL1BTM';
class AnalyticsCollector {
logger;
trackingEventsQueue;
requestParameterStringified;
userParameters;
constructor(logger, userId, packageManagerInfo) {
this.logger = logger;
const requestParameters = {
[analytics_parameters_1.RequestParameter.ProtocolVersion]: 2,
[analytics_parameters_1.RequestParameter.ClientId]: userId,
[analytics_parameters_1.RequestParameter.UserId]: userId,
[analytics_parameters_1.RequestParameter.TrackingId]: /^\d+\.\d+\.\d+$/.test(version_1.VERSION.full) && version_1.VERSION.full !== '0.0.0'
? TRACKING_ID_PROD
: TRACKING_ID_STAGING,
// Built-in user properties
[analytics_parameters_1.RequestParameter.SessionId]: (0, node_crypto_1.randomUUID)(),
[analytics_parameters_1.RequestParameter.UserAgentArchitecture]: os.arch(),
[analytics_parameters_1.RequestParameter.UserAgentPlatform]: os.platform(),
[analytics_parameters_1.RequestParameter.UserAgentPlatformVersion]: os.release(),
[analytics_parameters_1.RequestParameter.UserAgentMobile]: 0,
[analytics_parameters_1.RequestParameter.SessionEngaged]: 1,
// The below is needed for tech details to be collected.
[analytics_parameters_1.RequestParameter.UserAgentFullVersionList]: 'Google%20Chrome;111.0.5563.64|Not(A%3ABrand;8.0.0.0|Chromium;111.0.5563.64',
};
if (environment_options_1.ngDebug) {
requestParameters[analytics_parameters_1.RequestParameter.DebugView] = 1;
}
this.requestParameterStringified = querystring.stringify(requestParameters);
const parsedVersion = semver.parse(process.version);
const packageManagerVersion = packageManagerInfo.version;
this.userParameters = {
// While architecture is being collect by GA as UserAgentArchitecture.
// It doesn't look like there is a way to query this. Therefore we collect this as a custom user dimension too.
[analytics_parameters_1.UserCustomDimension.OsArchitecture]: os.arch(),
// While User ID is being collected by GA, this is not visible in reports/for filtering.
[analytics_parameters_1.UserCustomDimension.UserId]: userId,
[analytics_parameters_1.UserCustomDimension.NodeVersion]: parsedVersion
? `${parsedVersion.major}.${parsedVersion.minor}.${parsedVersion.patch}`
: 'other',
[analytics_parameters_1.UserCustomDimension.NodeMajorVersion]: parsedVersion?.major,
[analytics_parameters_1.UserCustomDimension.PackageManager]: packageManagerInfo.name,
[analytics_parameters_1.UserCustomDimension.PackageManagerVersion]: packageManagerVersion,
[analytics_parameters_1.UserCustomDimension.PackageManagerMajorVersion]: packageManagerVersion
? +packageManagerVersion.split('.', 1)[0]
: undefined,
[analytics_parameters_1.UserCustomDimension.AngularCLIVersion]: version_1.VERSION.full,
[analytics_parameters_1.UserCustomDimension.AngularCLIMajorVersion]: version_1.VERSION.major,
};
}
reportWorkspaceInfoEvent(parameters) {
this.event('workspace_info', parameters);
}
reportRebuildRunEvent(parameters) {
this.event('run_rebuild', parameters);
}
reportBuildRunEvent(parameters) {
this.event('run_build', parameters);
}
reportArchitectRunEvent(parameters) {
this.event('run_architect', parameters);
}
reportSchematicRunEvent(parameters) {
this.event('run_schematic', parameters);
}
reportCommandRunEvent(command) {
this.event('run_command', { [analytics_parameters_1.EventCustomDimension.Command]: command });
}
event(eventName, parameters) {
this.trackingEventsQueue ??= [];
this.trackingEventsQueue.push({
...this.userParameters,
...parameters,
'en': eventName,
});
}
/**
* Flush on an interval (if the event loop is waiting).
*
* @returns a method that when called will terminate the periodic
* flush and call flush one last time.
*/
periodFlush() {
let analyticsFlushPromise = Promise.resolve();
const analyticsFlushInterval = setInterval(() => {
if (this.trackingEventsQueue?.length) {
analyticsFlushPromise = analyticsFlushPromise.then(() => this.flush());
}
}, 4000);
return () => {
clearInterval(analyticsFlushInterval);
// Flush one last time.
return analyticsFlushPromise.then(() => this.flush());
};
}
async flush() {
const pendingTrackingEvents = this.trackingEventsQueue;
this.logger.debug(`Analytics flush size. ${pendingTrackingEvents?.length}.`);
if (!pendingTrackingEvents?.length) {
return;
}
// The below is needed so that if flush is called multiple times,
// we don't report the same event multiple times.
this.trackingEventsQueue = undefined;
try {
await this.send(pendingTrackingEvents);
}
catch (error) {
// Failure to report analytics shouldn't crash the CLI.
(0, error_1.assertIsError)(error);
this.logger.debug(`Send analytics error. ${error.message}.`);
}
}
async send(data) {
return new Promise((resolve, reject) => {
const request = https.request({
host: 'www.google-analytics.com',
method: 'POST',
path: '/g/collect?' + this.requestParameterStringified,
headers: {
// The below is needed for tech details to be collected even though we provide our own information from the OS Node.js module
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36',
},
}, (response) => {
// The below is needed as otherwise the response will never close which will cause the CLI not to terminate.
response.on('data', () => { });
if (response.statusCode !== 200 && response.statusCode !== 204) {
reject(new Error(`Analytics reporting failed with status code: ${response.statusCode}.`));
}
else {
resolve();
}
});
request.on('error', reject);
const queryParameters = data.map((p) => querystring.stringify(p)).join('\n');
request.write(queryParameters);
request.end();
});
}
}
exports.AnalyticsCollector = AnalyticsCollector;
//# sourceMappingURL=analytics-collector.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"analytics-collector.js","sourceRoot":"","sources":["analytics-collector.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGH,6CAAyC;AACzC,kDAAoC;AACpC,4CAA8B;AAC9B,8DAAgD;AAChD,+CAAiC;AACjC,0EAA2D;AAC3D,8CAAmD;AACnD,kDAA+C;AAC/C,iEAMgC;AAEhC,MAAM,gBAAgB,GAAG,cAAc,CAAC;AACxC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAE3C,MAAa,kBAAkB;IAMnB;IALF,mBAAmB,CAA2D;IACrE,2BAA2B,CAAS;IACpC,cAAc,CAA0D;IAEzF,YACU,MAAsB,EAC9B,MAAc,EACd,kBAAiE;QAFzD,WAAM,GAAN,MAAM,CAAgB;QAI9B,MAAM,iBAAiB,GAAsD;YAC3E,CAAC,uCAAgB,CAAC,eAAe,CAAC,EAAE,CAAC;YACrC,CAAC,uCAAgB,CAAC,QAAQ,CAAC,EAAE,MAAM;YACnC,CAAC,uCAAgB,CAAC,MAAM,CAAC,EAAE,MAAM;YACjC,CAAC,uCAAgB,CAAC,UAAU,CAAC,EAC3B,iBAAiB,CAAC,IAAI,CAAC,iBAAO,CAAC,IAAI,CAAC,IAAI,iBAAO,CAAC,IAAI,KAAK,OAAO;gBAC9D,CAAC,CAAC,gBAAgB;gBAClB,CAAC,CAAC,mBAAmB;YAEzB,2BAA2B;YAC3B,CAAC,uCAAgB,CAAC,SAAS,CAAC,EAAE,IAAA,wBAAU,GAAE;YAC1C,CAAC,uCAAgB,CAAC,qBAAqB,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE;YACnD,CAAC,uCAAgB,CAAC,iBAAiB,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE;YACnD,CAAC,uCAAgB,CAAC,wBAAwB,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE;YACzD,CAAC,uCAAgB,CAAC,eAAe,CAAC,EAAE,CAAC;YACrC,CAAC,uCAAgB,CAAC,cAAc,CAAC,EAAE,CAAC;YACpC,wDAAwD;YACxD,CAAC,uCAAgB,CAAC,wBAAwB,CAAC,EACzC,4EAA4E;SAC/E,CAAC;QAEF,IAAI,6BAAO,EAAE,CAAC;YACZ,iBAAiB,CAAC,uCAAgB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACpD,CAAC;QAED,IAAI,CAAC,2BAA2B,GAAG,WAAW,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAE5E,MAAM,aAAa,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,qBAAqB,GAAG,kBAAkB,CAAC,OAAO,CAAC;QAEzD,IAAI,CAAC,cAAc,GAAG;YACpB,sEAAsE;YACtE,+GAA+G;YAC/G,CAAC,0CAAmB,CAAC,cAAc,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE;YAC/C,wFAAwF;YACxF,CAAC,0CAAmB,CAAC,MAAM,CAAC,EAAE,MAAM;YACpC,CAAC,0CAAmB,CAAC,WAAW,CAAC,EAAE,aAAa;gBAC9C,CAAC,CAAC,GAAG,aAAa,CAAC,KAAK,IAAI,aAAa,CAAC,KAAK,IAAI,aAAa,CAAC,KAAK,EAAE;gBACxE,CAAC,CAAC,OAAO;YACX,CAAC,0CAAmB,CAAC,gBAAgB,CAAC,EAAE,aAAa,EAAE,KAAK;YAC5D,CAAC,0CAAmB,CAAC,cAAc,CAAC,EAAE,kBAAkB,CAAC,IAAI;YAC7D,CAAC,0CAAmB,CAAC,qBAAqB,CAAC,EAAE,qBAAqB;YAClE,CAAC,0CAAmB,CAAC,0BAA0B,CAAC,EAAE,qBAAqB;gBACrE,CAAC,CAAC,CAAC,qBAAqB,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;gBACzC,CAAC,CAAC,SAAS;YACb,CAAC,0CAAmB,CAAC,iBAAiB,CAAC,EAAE,iBAAO,CAAC,IAAI;YACrD,CAAC,0CAAmB,CAAC,sBAAsB,CAAC,EAAE,iBAAO,CAAC,KAAK;SAC5D,CAAC;IACJ,CAAC;IAED,wBAAwB,CACtB,UAAqF;QAErF,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;IAC3C,CAAC;IAED,qBAAqB,CACnB,UAEC;QAED,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;IACxC,CAAC;IAED,mBAAmB,CACjB,UAEC;QAED,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACtC,CAAC;IAED,uBAAuB,CAAC,UAAiE;QACvF,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IAC1C,CAAC;IAED,uBAAuB,CAAC,UAAiE;QACvF,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IAC1C,CAAC;IAED,qBAAqB,CAAC,OAAe;QACnC,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,EAAE,CAAC,2CAAoB,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IACzE,CAAC;IAEO,KAAK,CAAC,SAAiB,EAAE,UAA2C;QAC1E,IAAI,CAAC,mBAAmB,KAAK,EAAE,CAAC;QAChC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC;YAC5B,GAAG,IAAI,CAAC,cAAc;YACtB,GAAG,UAAU;YACb,IAAI,EAAE,SAAS;SAChB,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,WAAW;QACT,IAAI,qBAAqB,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;QAC9C,MAAM,sBAAsB,GAAG,WAAW,CAAC,GAAG,EAAE;YAC9C,IAAI,IAAI,CAAC,mBAAmB,EAAE,MAAM,EAAE,CAAC;gBACrC,qBAAqB,GAAG,qBAAqB,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YACzE,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,CAAC;QAET,OAAO,GAAG,EAAE;YACV,aAAa,CAAC,sBAAsB,CAAC,CAAC;YAEtC,uBAAuB;YACvB,OAAO,qBAAqB,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACxD,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,qBAAqB,GAAG,IAAI,CAAC,mBAAmB,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,qBAAqB,EAAE,MAAM,GAAG,CAAC,CAAC;QAE7E,IAAI,CAAC,qBAAqB,EAAE,MAAM,EAAE,CAAC;YACnC,OAAO;QACT,CAAC;QAED,iEAAiE;QACjE,iDAAiD;QACjD,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC;QAErC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,uDAAuD;YACvD,IAAA,qBAAa,EAAC,KAAK,CAAC,CAAC;YACrB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,IAAkD;QACnE,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAC3B;gBACE,IAAI,EAAE,0BAA0B;gBAChC,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,aAAa,GAAG,IAAI,CAAC,2BAA2B;gBACtD,OAAO,EAAE;oBACP,6HAA6H;oBAC7H,YAAY,EACV,uHAAuH;iBAC1H;aACF,EACD,CAAC,QAAQ,EAAE,EAAE;gBACX,4GAA4G;gBAC5G,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAE9B,IAAI,QAAQ,CAAC,UAAU,KAAK,GAAG,IAAI,QAAQ,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;oBAC/D,MAAM,CACJ,IAAI,KAAK,CAAC,gDAAgD,QAAQ,CAAC,UAAU,GAAG,CAAC,CAClF,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACN,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC,CACF,CAAC;YAEF,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC5B,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7E,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;YAC/B,OAAO,CAAC,GAAG,EAAE,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAnLD,gDAmLC"}
+101
View File
@@ -0,0 +1,101 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
/** Any changes in this file needs to be done in the mts version. */
export type PrimitiveTypes = string | number | boolean;
/**
* GA built-in request parameters
* @see https://www.thyngster.com/ga4-measurement-protocol-cheatsheet
* @see http://go/depot/google3/analytics/container_tag/templates/common/gold/mpv2_schema.js
*/
export declare enum RequestParameter {
ClientId = "cid",
DebugView = "_dbg",
GtmVersion = "gtm",
Language = "ul",
NewToSite = "_nsi",
NonInteraction = "ni",
PageLocation = "dl",
PageTitle = "dt",
ProtocolVersion = "v",
SessionEngaged = "seg",
SessionId = "sid",
SessionNumber = "sct",
SessionStart = "_ss",
TrackingId = "tid",
TrafficType = "tt",
UserAgentArchitecture = "uaa",
UserAgentBitness = "uab",
UserAgentFullVersionList = "uafvl",
UserAgentMobile = "uamb",
UserAgentModel = "uam",
UserAgentPlatform = "uap",
UserAgentPlatformVersion = "uapv",
UserId = "uid"
}
/**
* User scoped custom dimensions.
* @remarks
* - User custom dimensions limit is 25.
* - `up.*` string type.
* - `upn.*` number type.
* @see https://support.google.com/analytics/answer/10075209?hl=en
*/
export declare enum UserCustomDimension {
UserId = "up.ng_user_id",
OsArchitecture = "up.ng_os_architecture",
NodeVersion = "up.ng_node_version",
NodeMajorVersion = "upn.ng_node_major_version",
AngularCLIVersion = "up.ng_cli_version",
AngularCLIMajorVersion = "upn.ng_cli_major_version",
PackageManager = "up.ng_package_manager",
PackageManagerVersion = "up.ng_pkg_manager_version",
PackageManagerMajorVersion = "upn.ng_pkg_manager_major_v"
}
/**
* Event scoped custom dimensions.
* @remarks
* - Event custom dimensions limit is 50.
* - `ep.*` string type.
* - `epn.*` number type.
* @see https://support.google.com/analytics/answer/10075209?hl=en
*/
export declare enum EventCustomDimension {
Command = "ep.ng_command",
SchematicCollectionName = "ep.ng_schematic_collection_name",
SchematicName = "ep.ng_schematic_name",
Standalone = "ep.ng_standalone",
SSR = "ep.ng_ssr",
Style = "ep.ng_style",
Routing = "ep.ng_routing",
InlineTemplate = "ep.ng_inline_template",
InlineStyle = "ep.ng_inline_style",
BuilderTarget = "ep.ng_builder_target",
Aot = "ep.ng_aot",
Optimization = "ep.ng_optimization"
}
/**
* Event scoped custom mertics.
* @remarks
* - Event scoped custom mertics limit is 50.
* - `ep.*` string type.
* - `epn.*` number type.
* @see https://support.google.com/analytics/answer/10075209?hl=en
*/
export declare enum EventCustomMetric {
AllChunksCount = "epn.ng_all_chunks_count",
LazyChunksCount = "epn.ng_lazy_chunks_count",
InitialChunksCount = "epn.ng_initial_chunks_count",
ChangedChunksCount = "epn.ng_changed_chunks_count",
DurationInMs = "epn.ng_duration_ms",
CssSizeInBytes = "epn.ng_css_size_bytes",
JsSizeInBytes = "epn.ng_js_size_bytes",
NgComponentCount = "epn.ng_component_count",
AllProjectsCount = "epn.all_projects_count",
LibraryProjectsCount = "epn.libs_projects_count",
ApplicationProjectsCount = "epn.apps_projects_count"
}
+107
View File
@@ -0,0 +1,107 @@
"use strict";
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventCustomMetric = exports.EventCustomDimension = exports.UserCustomDimension = exports.RequestParameter = void 0;
/**
* GA built-in request parameters
* @see https://www.thyngster.com/ga4-measurement-protocol-cheatsheet
* @see http://go/depot/google3/analytics/container_tag/templates/common/gold/mpv2_schema.js
*/
var RequestParameter;
(function (RequestParameter) {
RequestParameter["ClientId"] = "cid";
RequestParameter["DebugView"] = "_dbg";
RequestParameter["GtmVersion"] = "gtm";
RequestParameter["Language"] = "ul";
RequestParameter["NewToSite"] = "_nsi";
RequestParameter["NonInteraction"] = "ni";
RequestParameter["PageLocation"] = "dl";
RequestParameter["PageTitle"] = "dt";
RequestParameter["ProtocolVersion"] = "v";
RequestParameter["SessionEngaged"] = "seg";
RequestParameter["SessionId"] = "sid";
RequestParameter["SessionNumber"] = "sct";
RequestParameter["SessionStart"] = "_ss";
RequestParameter["TrackingId"] = "tid";
RequestParameter["TrafficType"] = "tt";
RequestParameter["UserAgentArchitecture"] = "uaa";
RequestParameter["UserAgentBitness"] = "uab";
RequestParameter["UserAgentFullVersionList"] = "uafvl";
RequestParameter["UserAgentMobile"] = "uamb";
RequestParameter["UserAgentModel"] = "uam";
RequestParameter["UserAgentPlatform"] = "uap";
RequestParameter["UserAgentPlatformVersion"] = "uapv";
RequestParameter["UserId"] = "uid";
})(RequestParameter || (exports.RequestParameter = RequestParameter = {}));
/**
* User scoped custom dimensions.
* @remarks
* - User custom dimensions limit is 25.
* - `up.*` string type.
* - `upn.*` number type.
* @see https://support.google.com/analytics/answer/10075209?hl=en
*/
var UserCustomDimension;
(function (UserCustomDimension) {
UserCustomDimension["UserId"] = "up.ng_user_id";
UserCustomDimension["OsArchitecture"] = "up.ng_os_architecture";
UserCustomDimension["NodeVersion"] = "up.ng_node_version";
UserCustomDimension["NodeMajorVersion"] = "upn.ng_node_major_version";
UserCustomDimension["AngularCLIVersion"] = "up.ng_cli_version";
UserCustomDimension["AngularCLIMajorVersion"] = "upn.ng_cli_major_version";
UserCustomDimension["PackageManager"] = "up.ng_package_manager";
UserCustomDimension["PackageManagerVersion"] = "up.ng_pkg_manager_version";
UserCustomDimension["PackageManagerMajorVersion"] = "upn.ng_pkg_manager_major_v";
})(UserCustomDimension || (exports.UserCustomDimension = UserCustomDimension = {}));
/**
* Event scoped custom dimensions.
* @remarks
* - Event custom dimensions limit is 50.
* - `ep.*` string type.
* - `epn.*` number type.
* @see https://support.google.com/analytics/answer/10075209?hl=en
*/
var EventCustomDimension;
(function (EventCustomDimension) {
EventCustomDimension["Command"] = "ep.ng_command";
EventCustomDimension["SchematicCollectionName"] = "ep.ng_schematic_collection_name";
EventCustomDimension["SchematicName"] = "ep.ng_schematic_name";
EventCustomDimension["Standalone"] = "ep.ng_standalone";
EventCustomDimension["SSR"] = "ep.ng_ssr";
EventCustomDimension["Style"] = "ep.ng_style";
EventCustomDimension["Routing"] = "ep.ng_routing";
EventCustomDimension["InlineTemplate"] = "ep.ng_inline_template";
EventCustomDimension["InlineStyle"] = "ep.ng_inline_style";
EventCustomDimension["BuilderTarget"] = "ep.ng_builder_target";
EventCustomDimension["Aot"] = "ep.ng_aot";
EventCustomDimension["Optimization"] = "ep.ng_optimization";
})(EventCustomDimension || (exports.EventCustomDimension = EventCustomDimension = {}));
/**
* Event scoped custom mertics.
* @remarks
* - Event scoped custom mertics limit is 50.
* - `ep.*` string type.
* - `epn.*` number type.
* @see https://support.google.com/analytics/answer/10075209?hl=en
*/
var EventCustomMetric;
(function (EventCustomMetric) {
EventCustomMetric["AllChunksCount"] = "epn.ng_all_chunks_count";
EventCustomMetric["LazyChunksCount"] = "epn.ng_lazy_chunks_count";
EventCustomMetric["InitialChunksCount"] = "epn.ng_initial_chunks_count";
EventCustomMetric["ChangedChunksCount"] = "epn.ng_changed_chunks_count";
EventCustomMetric["DurationInMs"] = "epn.ng_duration_ms";
EventCustomMetric["CssSizeInBytes"] = "epn.ng_css_size_bytes";
EventCustomMetric["JsSizeInBytes"] = "epn.ng_js_size_bytes";
EventCustomMetric["NgComponentCount"] = "epn.ng_component_count";
EventCustomMetric["AllProjectsCount"] = "epn.all_projects_count";
EventCustomMetric["LibraryProjectsCount"] = "epn.libs_projects_count";
EventCustomMetric["ApplicationProjectsCount"] = "epn.apps_projects_count";
})(EventCustomMetric || (exports.EventCustomMetric = EventCustomMetric = {}));
//# sourceMappingURL=analytics-parameters.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"analytics-parameters.js","sourceRoot":"","sources":["analytics-parameters.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAMH;;;;GAIG;AACH,IAAY,gBAwBX;AAxBD,WAAY,gBAAgB;IAC1B,oCAAgB,CAAA;IAChB,sCAAkB,CAAA;IAClB,sCAAkB,CAAA;IAClB,mCAAe,CAAA;IACf,sCAAkB,CAAA;IAClB,yCAAqB,CAAA;IACrB,uCAAmB,CAAA;IACnB,oCAAgB,CAAA;IAChB,yCAAqB,CAAA;IACrB,0CAAsB,CAAA;IACtB,qCAAiB,CAAA;IACjB,yCAAqB,CAAA;IACrB,wCAAoB,CAAA;IACpB,sCAAkB,CAAA;IAClB,sCAAkB,CAAA;IAClB,iDAA6B,CAAA;IAC7B,4CAAwB,CAAA;IACxB,sDAAkC,CAAA;IAClC,4CAAwB,CAAA;IACxB,0CAAsB,CAAA;IACtB,6CAAyB,CAAA;IACzB,qDAAiC,CAAA;IACjC,kCAAc,CAAA;AAChB,CAAC,EAxBW,gBAAgB,gCAAhB,gBAAgB,QAwB3B;AAED;;;;;;;GAOG;AACH,IAAY,mBAUX;AAVD,WAAY,mBAAmB;IAC7B,+CAAwB,CAAA;IACxB,+DAAwC,CAAA;IACxC,yDAAkC,CAAA;IAClC,qEAA8C,CAAA;IAC9C,8DAAuC,CAAA;IACvC,0EAAmD,CAAA;IACnD,+DAAwC,CAAA;IACxC,0EAAmD,CAAA;IACnD,gFAAyD,CAAA;AAC3D,CAAC,EAVW,mBAAmB,mCAAnB,mBAAmB,QAU9B;AAED;;;;;;;GAOG;AACH,IAAY,oBAaX;AAbD,WAAY,oBAAoB;IAC9B,iDAAyB,CAAA;IACzB,mFAA2D,CAAA;IAC3D,8DAAsC,CAAA;IACtC,uDAA+B,CAAA;IAC/B,yCAAiB,CAAA;IACjB,6CAAqB,CAAA;IACrB,iDAAyB,CAAA;IACzB,gEAAwC,CAAA;IACxC,0DAAkC,CAAA;IAClC,8DAAsC,CAAA;IACtC,yCAAiB,CAAA;IACjB,2DAAmC,CAAA;AACrC,CAAC,EAbW,oBAAoB,oCAApB,oBAAoB,QAa/B;AAED;;;;;;;GAOG;AACH,IAAY,iBAYX;AAZD,WAAY,iBAAiB;IAC3B,+DAA0C,CAAA;IAC1C,iEAA4C,CAAA;IAC5C,uEAAkD,CAAA;IAClD,uEAAkD,CAAA;IAClD,wDAAmC,CAAA;IACnC,6DAAwC,CAAA;IACxC,2DAAsC,CAAA;IACtC,gEAA2C,CAAA;IAC3C,gEAA2C,CAAA;IAC3C,qEAAgD,CAAA;IAChD,yEAAoD,CAAA;AACtD,CAAC,EAZW,iBAAiB,iCAAjB,iBAAiB,QAY5B"}
+27
View File
@@ -0,0 +1,27 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import type { CommandContext } from '../command-builder/definitions';
/**
* This is the ultimate safelist for checking if a package name is safe to report to analytics.
*/
export declare const analyticsPackageSafelist: (string | RegExp)[];
export declare function isPackageNameSafeForAnalytics(name: string): boolean;
/**
* Set analytics settings. This does not work if the user is not inside a project.
* @param global Which config to use. "global" for user-level, and "local" for project-level.
* @param value Either a user ID, true to generate a new User ID, or false to disable analytics.
*/
export declare function setAnalyticsConfig(global: boolean, value: string | boolean): Promise<void>;
/**
* Prompt the user for usage gathering permission.
* @param force Whether to ask regardless of whether or not the user is using an interactive shell.
* @return Whether or not the user was shown a prompt.
*/
export declare function promptAnalytics(context: CommandContext, global: boolean, force?: boolean): Promise<boolean>;
export declare function getAnalyticsUserId(context: CommandContext, skipPrompt?: boolean): Promise<string | undefined>;
export declare function getAnalyticsInfoString(context: CommandContext): Promise<string>;
+183
View File
@@ -0,0 +1,183 @@
"use strict";
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.analyticsPackageSafelist = void 0;
exports.isPackageNameSafeForAnalytics = isPackageNameSafeForAnalytics;
exports.setAnalyticsConfig = setAnalyticsConfig;
exports.promptAnalytics = promptAnalytics;
exports.getAnalyticsUserId = getAnalyticsUserId;
exports.getAnalyticsInfoString = getAnalyticsInfoString;
const core_1 = require("@angular-devkit/core");
const node_crypto_1 = require("node:crypto");
const color_1 = require("../utilities/color");
const config_1 = require("../utilities/config");
const environment_options_1 = require("../utilities/environment-options");
const prompt_1 = require("../utilities/prompt");
const tty_1 = require("../utilities/tty");
/* eslint-disable no-console */
/**
* This is the ultimate safelist for checking if a package name is safe to report to analytics.
*/
exports.analyticsPackageSafelist = [
/^@angular\//,
/^@angular-devkit\//,
/^@nguniversal\//,
'@schematics/angular',
];
function isPackageNameSafeForAnalytics(name) {
return exports.analyticsPackageSafelist.some((pattern) => {
if (typeof pattern == 'string') {
return pattern === name;
}
else {
return pattern.test(name);
}
});
}
/**
* Set analytics settings. This does not work if the user is not inside a project.
* @param global Which config to use. "global" for user-level, and "local" for project-level.
* @param value Either a user ID, true to generate a new User ID, or false to disable analytics.
*/
async function setAnalyticsConfig(global, value) {
const level = global ? 'global' : 'local';
const workspace = await (0, config_1.getWorkspace)(level);
if (!workspace) {
throw new Error(`Could not find ${level} workspace.`);
}
const cli = (workspace.extensions['cli'] ??= {});
if (!workspace || !core_1.json.isJsonObject(cli)) {
throw new Error(`Invalid config found at ${workspace.filePath}. CLI should be an object.`);
}
cli.analytics = value === true ? (0, node_crypto_1.randomUUID)() : value;
await workspace.save();
}
/**
* Prompt the user for usage gathering permission.
* @param force Whether to ask regardless of whether or not the user is using an interactive shell.
* @return Whether or not the user was shown a prompt.
*/
async function promptAnalytics(context, global, force = false) {
const level = global ? 'global' : 'local';
const workspace = await (0, config_1.getWorkspace)(level);
if (!workspace) {
throw new Error(`Could not find a ${level} workspace. Are you in a project?`);
}
if (force || (0, tty_1.isTTY)()) {
const answer = await (0, prompt_1.askConfirmation)(`
Would you like to share pseudonymous usage data about this project with the Angular Team
at Google under Google's Privacy Policy at https://policies.google.com/privacy. For more
details and how to change this setting, see https://angular.dev/cli/analytics.
`, false);
await setAnalyticsConfig(global, answer);
if (answer) {
console.log('');
console.log(core_1.tags.stripIndent `
Thank you for sharing pseudonymous usage data. Should you change your mind, the following
command will disable this feature entirely:
${color_1.colors.yellow(`ng analytics disable${global ? ' --global' : ''}`)}
`);
console.log('');
}
process.stderr.write(await getAnalyticsInfoString(context));
return true;
}
return false;
}
/**
* Get the analytics user id.
*
* @returns
* - `string` user id.
* - `false` when disabled.
* - `undefined` when not configured.
*/
async function getAnalyticsUserIdForLevel(level) {
if (environment_options_1.analyticsDisabled) {
return false;
}
const workspace = await (0, config_1.getWorkspace)(level);
const analyticsConfig = workspace?.getCli()?.['analytics'];
if (analyticsConfig === false) {
return false;
}
else if (analyticsConfig === undefined || analyticsConfig === null) {
return undefined;
}
else {
if (typeof analyticsConfig == 'string') {
return analyticsConfig;
}
else if (typeof analyticsConfig == 'object' && typeof analyticsConfig['uid'] == 'string') {
return analyticsConfig['uid'];
}
return undefined;
}
}
async function getAnalyticsUserId(context, skipPrompt = false) {
const { workspace } = context;
// Global config takes precedence over local config only for the disabled check.
// IE:
// global: disabled & local: enabled = disabled
// global: id: 123 & local: id: 456 = 456
// check global
const globalConfig = await getAnalyticsUserIdForLevel('global');
if (globalConfig === false) {
return undefined;
}
// Not disabled globally, check locally or not set globally and command is run outside of workspace example: `ng new`
if (workspace || globalConfig === undefined) {
const level = workspace ? 'local' : 'global';
let localOrGlobalConfig = await getAnalyticsUserIdForLevel(level);
if (localOrGlobalConfig === undefined) {
if (!skipPrompt) {
// config is unset, prompt user.
// TODO: This should honor the `no-interactive` option.
// It is currently not an `ng` option but rather only an option for specific commands.
// The concept of `ng`-wide options are needed to cleanly handle this.
await promptAnalytics(context, !workspace /** global */);
localOrGlobalConfig = await getAnalyticsUserIdForLevel(level);
}
}
if (localOrGlobalConfig === false) {
return undefined;
}
else if (typeof localOrGlobalConfig === 'string') {
return localOrGlobalConfig;
}
}
return globalConfig;
}
function analyticsConfigValueToHumanFormat(value) {
if (value === false) {
return 'disabled';
}
else if (typeof value === 'string' || value === true) {
return 'enabled';
}
else {
return 'not set';
}
}
async function getAnalyticsInfoString(context) {
const analyticsInstance = await getAnalyticsUserId(context, true /** skipPrompt */);
const { globalConfiguration, workspace: localWorkspace } = context;
const globalSetting = globalConfiguration?.getCli()?.['analytics'];
const localSetting = localWorkspace?.getCli()?.['analytics'];
return (core_1.tags.stripIndents `
Global setting: ${analyticsConfigValueToHumanFormat(globalSetting)}
Local setting: ${localWorkspace
? analyticsConfigValueToHumanFormat(localSetting)
: 'No local workspace configuration file.'}
Effective status: ${analyticsInstance ? 'enabled' : 'disabled'}
` + '\n');
}
//# sourceMappingURL=analytics.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["analytics.ts"],"names":[],"mappings":"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"}