Skip to content

Commit 331c1dc

Browse files
chore: correctly resolve url sass bundle in Angular CT (#25191)
Co-authored-by: Zachary Williams <[email protected]>
1 parent cd4bc74 commit 331c1dc

File tree

8 files changed

+73
-17
lines changed

8 files changed

+73
-17
lines changed

npm/webpack-dev-server/src/helpers/angularHandler.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import * as fs from 'fs-extra'
22
import { tmpdir } from 'os'
33
import * as path from 'path'
4-
import type { Configuration } from 'webpack'
4+
import type { Configuration, RuleSetRule } from 'webpack'
55
import type { PresetHandlerResult, WebpackDevServerConfig } from '../devServer'
66
import { dynamicAbsoluteImport, dynamicImport } from '../dynamic-import'
77
import { sourceDefaultWebpackDependencies } from './sourceRelativeWebpackModules'
8+
import debugLib from 'debug'
9+
10+
const debug = debugLib('cypress:webpack-dev-server:angularHandler')
811

912
export type BuildOptions = Record<string, any>
1013

@@ -250,7 +253,33 @@ async function getAngularCliWebpackConfig (devServerConfig: AngularWebpackDevSer
250253
const { config } = await generateBrowserWebpackConfigFromContext(
251254
buildOptions,
252255
context,
253-
(wco: any) => [getCommonConfig(wco), getStylesConfig(wco)],
256+
(wco: any) => {
257+
const stylesConfig = getStylesConfig(wco)
258+
259+
// We modify resolve-url-loader and set `root` to be `projectRoot` + `sourceRoot` to ensure
260+
// imports in scss, sass, etc are correctly resolved.
261+
// https://github.com/cypress-io/cypress/issues/24272
262+
stylesConfig.module.rules.forEach((rule: RuleSetRule) => {
263+
rule.rules?.forEach((ruleSet) => {
264+
if (!Array.isArray(ruleSet.use)) {
265+
return
266+
}
267+
268+
ruleSet.use.map((loader) => {
269+
if (typeof loader !== 'object' || typeof loader.options !== 'object' || !loader.loader?.includes('resolve-url-loader')) {
270+
return
271+
}
272+
273+
const root = path.join(devServerConfig.cypressConfig.projectRoot, projectConfig.sourceRoot)
274+
275+
debug('Adding root %s to resolve-url-loader options', root)
276+
loader.options.root = path.join(devServerConfig.cypressConfig.projectRoot, projectConfig.sourceRoot)
277+
})
278+
})
279+
})
280+
281+
return [getCommonConfig(wco), stylesConfig]
282+
},
254283
)
255284

256285
delete config.entry.main

system-tests/project-fixtures/angular/src/app/app.component.cy.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Keep this test very simple as "@cypress/schematic" relies on it to run smoke tests
12
import { AppComponent } from './app.component'
23

34
it('should', () => {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<h1>Hello World your app is running!</h1>
1+
<h1>Hello World your app is running!</h1>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
h1 {
22
color: red
3-
}
3+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.test-img {
2+
background-image: url("/assets/test.png");
3+
background-size: contain;
4+
height: 100px;
5+
width: 100px;
6+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Component } from '@angular/core'
2+
3+
@Component({
4+
selector: 'app-url-image',
5+
template: `<div class="test-img"></div>`,
6+
styleUrls: ['./url-image.component.scss'],
7+
})
8+
export class UrlImageComponent {}

system-tests/project-fixtures/angular/src/app/mount.cy.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { LogoComponent } from './components/logo.component'
2020
import { TransientService, TransientServicesComponent } from './components/transient-services.component'
2121
import { ComponentProviderComponent, MessageService } from './components/component-provider.component'
2222
import { Cart, ProductComponent } from './components/cart.component'
23+
import { UrlImageComponent } from './components/url-image.component'
2324

2425
@Component({
2526
template: `<app-projection>Hello World</app-projection>`,
@@ -360,9 +361,20 @@ describe('angular mount', () => {
360361
cy.get('p').should('have.text', 'Hi . ngOnInit fired: true and ngOnChanges fired: false and conditionalName: false')
361362
})
362363

363-
it('can load static assets', () => {
364-
cy.mount(LogoComponent)
365-
cy.get('img').should('be.visible').and('have.prop', 'naturalWidth').should('be.greaterThan', 0)
364+
context('assets', () => {
365+
it('can load static assets from <img src="..." />', () => {
366+
cy.mount(LogoComponent)
367+
cy.get('img').should('be.visible').and('have.prop', 'naturalWidth').should('be.greaterThan', 0)
368+
})
369+
370+
it('can load root relative scss "url()" assets', () => {
371+
cy.mount(UrlImageComponent)
372+
cy.get('.test-img')
373+
.invoke('css', 'background-image')
374+
.then((img) => {
375+
expect(img).to.contain('__cypress/src/test.png')
376+
})
377+
})
366378
})
367379

368380
context('dependency injection', () => {
@@ -430,27 +442,27 @@ describe('angular mount', () => {
430442

431443
describe('teardown', () => {
432444
beforeEach(() => {
433-
cy.get("[id^=root]").should("not.exist");
434-
});
445+
cy.get('[id^=root]').should('not.exist')
446+
})
435447

436-
it("should mount", () => {
437-
cy.mount(ButtonOutputComponent);
438-
});
448+
it('should mount', () => {
449+
cy.mount(ButtonOutputComponent)
450+
})
439451

440452
it('should remove previous mounted component', () => {
441-
cy.mount(ChildComponent, {componentProperties: { msg: 'Render 1' }})
453+
cy.mount(ChildComponent, { componentProperties: { msg: 'Render 1' } })
442454
cy.contains('Render 1')
443-
cy.mount(ChildComponent, {componentProperties: { msg: 'Render 2' }})
455+
cy.mount(ChildComponent, { componentProperties: { msg: 'Render 2' } })
444456
cy.contains('Render 2')
445457

446458
cy.contains('Render 1').should('not.exist')
447459
cy.get('[id^=root]').children().should('have.length', 1)
448460
})
449-
});
461+
})
450462

451463
it('should error when passing in undecorated component', () => {
452464
Cypress.on('fail', (err) => {
453-
expect(err.message).contain("Please add a @Pipe/@Directive/@Component");
465+
expect(err.message).contain('Please add a @Pipe/@Directive/@Component')
454466

455467
return false
456468
})
@@ -459,4 +471,4 @@ describe('angular mount', () => {
459471

460472
cy.mount(MyClass)
461473
})
462-
});
474+
})
118 KB
Loading

0 commit comments

Comments
 (0)