Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 101 additions & 107 deletions e2e-tests/tests/angular-19.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,49 @@ import {
checkIfRunsOnProdMode,
checkPackageJson,
cleanupGit,
getWizardCommand,
initGit,
KEYS,
modifyFile,
revertLocalChanges,
startWizardInstance,
} from '../utils';
import * as path from 'path';
import { TEST_ARGS } from '../utils';
import { test, expect, describe, beforeAll, afterAll } from 'vitest';

describe.sequential('Angular-19', () => {
//@ts-expect-error - clifty is ESM only
import { withEnv } from 'clifty';

// eslint-disable-next-line vitest/valid-describe-callback
describe.sequential('Angular-19', { retry: 0 }, () => {
describe('with empty project', () => {
const integration = Integration.angular;
const projectDir = path.resolve(
__dirname,
'../test-applications/angular-19-test-app',
);

beforeAll(async () => {
initGit(projectDir);
revertLocalChanges(projectDir);
await runWizardOnAngularProject(projectDir, integration);
await runWizardOnAngularProject(projectDir);
});

afterAll(() => {
revertLocalChanges(projectDir);
cleanupGit(projectDir);
});

checkAngularProject(projectDir, integration);
checkAngularProject(projectDir);
});
describe('with pre-defined ErrorHandler', () => {
const integration = Integration.angular;
const projectDir = path.resolve(
__dirname,
'../test-applications/angular-19-test-app',
);

beforeAll(async () => {
revertLocalChanges(projectDir);
await runWizardOnAngularProject(projectDir, integration, (projectDir) => {
await runWizardOnAngularProject(projectDir, (projectDir) => {
modifyFile(`${projectDir}/src/app/app.config.ts`, {
'providers: [': `providers: [{
provide: ErrorHandler,
Expand All @@ -62,131 +66,121 @@ describe.sequential('Angular-19', () => {
cleanupGit(projectDir);
});

checkAngularProject(projectDir, integration, {
checkAngularProject(projectDir, {
preExistingErrorHandler: true,
});
});
});

async function runWizardOnAngularProject(
projectDir: string,
integration: Integration,
fileModificationFn?: (projectDir: string) => unknown,
) {
const wizardInstance = startWizardInstance(integration, projectDir, true);
const interactionBuilder = withEnv({
cwd: projectDir,
debug: true,
}).defineInteraction();

if (fileModificationFn) {
fileModificationFn(projectDir);

await wizardInstance.waitForOutput('Do you want to continue anyway?');

await wizardInstance.sendStdinAndWaitForOutput(
[KEYS.ENTER],
'Please select your package manager.',
interactionBuilder.step(
'confirm continue with dirty repo',
({ whenAsked }) => {
whenAsked('Do you want to continue anyway?').respondWith(KEYS.ENTER);
},
);
} else {
await wizardInstance.waitForOutput('Please select your package manager.');
}

await wizardInstance.sendStdinAndWaitForOutput(
// Selecting `yarn` as the package manager
[KEYS.DOWN, KEYS.ENTER],
// "Do you want to enable Tracing", sometimes doesn't work as `Tracing` can be printed in bold.
'to track the performance of your application?',
{
timeout: 240_000,
optional: true,
},
);

await wizardInstance.sendStdinAndWaitForOutput(
[KEYS.ENTER],
// "Do you want to enable Sentry Session Replay", sometimes doesn't work as `Sentry Session Replay` can be printed in bold.
'to get a video-like reproduction of errors during a user session?',
);

await wizardInstance.sendStdinAndWaitForOutput(
// The first choice here is Angular
[KEYS.ENTER],
'Where are your build artifacts located?',
{
optional: true,
timeout: 5000,
},
);

const sourcemapsConfiguredPromise = wizardInstance.waitForOutput(
'Added a sentry:sourcemaps script to your package.json',
);

const buildScriptPromptedPromise = wizardInstance.waitForOutput(
'Do you want to automatically run the sentry:sourcemaps script after each production build?',
);

const optionalArtifactsNotFoundPromise = wizardInstance.waitForOutput(
"We couldn't find artifacts",
{
optional: true,
timeout: 5000,
},
);

// ./dist is the default value, no need to change it
wizardInstance.sendStdin(KEYS.ENTER);

const optionalArtifactsNotFoundPrompted =
await optionalArtifactsNotFoundPromise;

if (optionalArtifactsNotFoundPrompted) {
wizardInstance.sendStdin(KEYS.DOWN);
wizardInstance.sendStdin(KEYS.ENTER);
}

await sourcemapsConfiguredPromise;
await buildScriptPromptedPromise;

await wizardInstance.sendStdinAndWaitForOutput(
[KEYS.ENTER], // yes, automatically add sentry:sourcemaps script
'Is yarn build your production build command?',
);

await wizardInstance.sendStdinAndWaitForOutput(
[KEYS.ENTER], // yes, yarn build is the production build command
'Are you using a CI/CD tool to build and deploy your application?',
);

await wizardInstance.sendStdinAndWaitForOutput(
[KEYS.DOWN, KEYS.ENTER], // no CI/CD tool
'Do you want to create an example component to test your Sentry setup?',
);

await wizardInstance.sendStdinAndWaitForOutput(
[KEYS.ENTER], // yes, create example component
'Did you apply the snippet above?',
);

await wizardInstance.sendStdinAndWaitForOutput(
[KEYS.ENTER], // yes, applied the snippet
'Looks like you have Prettier in your project. Do you want to run it on your files?',
);

await wizardInstance.sendStdinAndWaitForOutput(
[KEYS.ENTER], // yes, run prettier
'Successfully installed the Sentry Angular SDK!',
);

wizardInstance.kill();
await interactionBuilder
.step('select package manager', ({ expectOutput, whenAsked }) => {
expectOutput('Please select your package manager.');
whenAsked('Please select your package manager.').respondWith(
KEYS.DOWN,
KEYS.ENTER,
);
expectOutput('Installing @sentry/angular');
})
.step('feature selection', ({ whenAsked, expectOutput }) => {
whenAsked('to track the performance of your application?', {
// 1st question after package installation
timeout: 240_000,
}).respondWith(KEYS.ENTER);

whenAsked(
'to get a video-like reproduction of errors during a user session?',
).respondWith(KEYS.ENTER);

expectOutput('Successfully initialized Sentry on main.ts');

if (fileModificationFn) {
expectOutput(
'ErrorHandler provider already exists in your app config.',
);
expectOutput(
'https://docs.sentry.io/platforms/javascript/guides/angular/features/error-handler/',
);
}

expectOutput('Successfully updated your app config app.config.ts');
})
.step('source maps', ({ whenAsked, expectOutput }) => {
expectOutput('Installing @sentry/cli');

// .dist is the default value, no need to change it
whenAsked('Where are your build artifacts located?', {
// installing @sentry/cli takes a while
timeout: 240_000,
})
.respondWith(KEYS.ENTER)
.expectOutput('dist');

// no build artifacts found when running the wizard without a prior build
whenAsked(
'Are you sure that this is the location that contains your build artifacts?',
).respondWith(KEYS.DOWN, KEYS.ENTER);

whenAsked(
'Do you want to automatically run the sentry:sourcemaps script after each production build?',
).respondWith(KEYS.ENTER);

expectOutput('Added a sentry:sourcemaps script to your package.json');

whenAsked('Is yarn build your production build command?').respondWith(
KEYS.ENTER,
);

whenAsked(
'Are you using a CI/CD tool to build and deploy your application?',
).respondWith(
// no CI/CD tool (no need to show the token in the test)
KEYS.DOWN,
KEYS.ENTER,
);
})
.step('create example component', ({ whenAsked }) => {
whenAsked(
'Do you want to create an example component to test your Sentry setup?',
).respondWith(KEYS.ENTER);

whenAsked('Did you apply the snippet above?').respondWith(KEYS.ENTER);
})
.whenAsked(
'Looks like you have Prettier in your project. Do you want to run it on your files?',
)
.respondWith(KEYS.ENTER)
.expectOutput('Successfully installed the Sentry Angular SDK!')
.run(getWizardCommand(Integration.angular));
}

function checkAngularProject(
projectDir: string,
integration: Integration,
options?: {
preExistingErrorHandler?: boolean;
},
) {
test('package.json is updated correctly', () => {
checkPackageJson(projectDir, integration);
checkPackageJson(projectDir, Integration.angular);

const packageJsonFile = path.resolve(projectDir, 'package.json');
checkFileContents(packageJsonFile, [
Expand Down
1 change: 1 addition & 0 deletions e2e-tests/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@
const contentArray = Array.isArray(content) ? content : [content];

for (const c of contentArray) {
expect(fileContent).toContain(c);

Check failure on line 404 in e2e-tests/utils/index.ts

View workflow job for this annotation

GitHub Actions / Angular-19 E2E Tests (ubuntu-latest)

tests/angular-19.test.ts > Angular-19 > with pre-defined ErrorHandler > Sentry is correctly injected into Angular app module

AssertionError: expected 'import * as Sentry from "@sentry/angu…' to contain 'import * as Sentry from \'@sentry/ang…' - Expected + Received - import * as Sentry from '@sentry/angular' + import * as Sentry from "@sentry/angular"; + import { + ApplicationConfig, + provideZoneChangeDetection, + ErrorHandler, + provideAppInitializer, + inject, + } from '@angular/core'; + import { provideRouter, Router } from '@angular/router'; + + import { routes } from './app.routes'; + + export const appConfig: ApplicationConfig = { + providers: [{ + provide: ErrorHandler, + useValue: null + }, provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), { + provide: Sentry.TraceService, + deps: [Router] + }, provideAppInitializer(() => { + inject(Sentry.TraceService); + })] + }; ❯ checkFileContents utils/index.ts:404:25 ❯ tests/angular-19.test.ts:218:5

Check failure on line 404 in e2e-tests/utils/index.ts

View workflow job for this annotation

GitHub Actions / Angular-19 E2E Tests (ubuntu-latest)

tests/angular-19.test.ts > Angular-19 > with pre-defined ErrorHandler > Sentry is correctly injected into Angular app config

AssertionError: expected 'import * as Sentry from "@sentry/angu…' to contain 'import * as Sentry from \'@sentry/ang…' - Expected + Received - import * as Sentry from '@sentry/angular' + import * as Sentry from "@sentry/angular"; + import { bootstrapApplication } from '@angular/platform-browser'; + import { appConfig } from './app/app.config'; + import { AppComponent } from './app/app.component'; + + Sentry.init({ + dsn: "https://[email protected]/1337", + integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()], + tracesSampleRate: 1, + replaysSessionSampleRate: 0.1, + replaysOnErrorSampleRate: 1 + }) + + bootstrapApplication(AppComponent, appConfig) + .catch((err) => console.error(err)); ❯ checkFileContents utils/index.ts:404:25 ❯ tests/angular-19.test.ts:196:5

Check failure on line 404 in e2e-tests/utils/index.ts

View workflow job for this annotation

GitHub Actions / Angular-19 E2E Tests (macos-15)

tests/angular-19.test.ts > Angular-19 > with pre-defined ErrorHandler > Sentry is correctly injected into Angular app module

AssertionError: expected 'import * as Sentry from "@sentry/angu…' to contain 'import * as Sentry from \'@sentry/ang…' - Expected + Received - import * as Sentry from '@sentry/angular' + import * as Sentry from "@sentry/angular"; + import { + ApplicationConfig, + provideZoneChangeDetection, + ErrorHandler, + provideAppInitializer, + inject, + } from '@angular/core'; + import { provideRouter, Router } from '@angular/router'; + + import { routes } from './app.routes'; + + export const appConfig: ApplicationConfig = { + providers: [{ + provide: ErrorHandler, + useValue: null + }, provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), { + provide: Sentry.TraceService, + deps: [Router] + }, provideAppInitializer(() => { + inject(Sentry.TraceService); + })] + }; ❯ checkFileContents utils/index.ts:404:25 ❯ tests/angular-19.test.ts:218:5

Check failure on line 404 in e2e-tests/utils/index.ts

View workflow job for this annotation

GitHub Actions / Angular-19 E2E Tests (macos-15)

tests/angular-19.test.ts > Angular-19 > with pre-defined ErrorHandler > Sentry is correctly injected into Angular app config

AssertionError: expected 'import * as Sentry from "@sentry/angu…' to contain 'import * as Sentry from \'@sentry/ang…' - Expected + Received - import * as Sentry from '@sentry/angular' + import * as Sentry from "@sentry/angular"; + import { bootstrapApplication } from '@angular/platform-browser'; + import { appConfig } from './app/app.config'; + import { AppComponent } from './app/app.component'; + + Sentry.init({ + dsn: "https://[email protected]/1337", + integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()], + tracesSampleRate: 1, + replaysSessionSampleRate: 0.1, + replaysOnErrorSampleRate: 1 + }) + + bootstrapApplication(AppComponent, appConfig) + .catch((err) => console.error(err)); ❯ checkFileContents utils/index.ts:404:25 ❯ tests/angular-19.test.ts:196:5
}
}

Expand Down Expand Up @@ -465,6 +465,7 @@
*/
export async function checkIfBuilds(projectDir: string) {
const testEnv = new WizardTestEnv('npm', ['run', 'build'], {
debug: true,
cwd: projectDir,
});

Expand Down
Loading