bahmutov / cypress-angular-unit-test

Trying to load and bootstrap Angular component dynamically inside Cypress
159 stars 32 forks source link

Loading styles (CSS/SCSS/SASS) stored under assets for all components #184

Open azaeng04 opened 4 years ago

azaeng04 commented 4 years ago

Hi @LeJeanbono this is a question in regards to the assets. At the moment the project I am trying to get to load assets for all components with styles stored in subfolders under assets. Now I am not sure exactly what each component requires, but is it possible to load all those assets stored in subfolders of subfolders and bundle them up. Cos at the moment I need to do the following:


        "src/app/login/login.component.scss",
        // "src/assets/styles/main.scss",
        // "src/assets/styles/partials/_all.scss",
        // "src/assets/styles/base/_all.scss",
        "node_modules/primeng/resources/primeng.min.css",
        "node_modules/@fullcalendar/core/main.css",
        "node_modules/@fullcalendar/daygrid/main.css",
        "node_modules/quill/dist/quill.snow.css",
        "node_modules/primeicons/primeicons.css",
        "src/assets/styles/variables.css",
      ], stylesheets: [
        "/assets/monaco/vs/loader.js",
        "/assets/styles/partials/_common.scss",
        "/assets/styles/partials/_dashboard.scss",
        "/assets/styles/partials/_forms.scss",
        "/assets/styles/partials/_icons.scss",
        "/assets/styles/partials/_login.scss",
        "/assets/styles/partials/_main.scss",
        "/assets/styles/partials/_menu.scss",
        "/assets/styles/partials/_message.scss",
        "/assets/styles/partials/_misc.scss",
        "/assets/styles/partials/_overlay.scss",
        "/assets/styles/partials/_panel.scss",
        "/assets/styles/partials/_quick-actions.scss",
        "/assets/styles/partials/_splash.scss",
        "/assets/styles/partials/_utils.scss",
        "/assets/styles/partials/_data.scss",
        "/assets/styles/base/_common.scss",
        "/assets/styles/base/_defaults.scss",
        "/assets/styles/base/_fonts.scss",
        "/assets/styles/base/_layout.scss",
        "/styles.scss",
      ], detectChanges: true });```
LeJeanbono commented 4 years ago

Do you have all those styles referenced, in angular.json ?

azaeng04 commented 4 years ago

No they are not referenced there. This is what the angular.json looks like: @LeJeanbono

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "voss-portal": {
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "aot": true,
            "outputPath": "dist",
            "index": "src/index.html",
            "main": "src/main.ts",
            "tsConfig": "src/tsconfig.app.json",
            "polyfills": "src/polyfills.ts",
            "assets": [
              "src/assets",
              "src/upload.php",
              "src/favicon.ico"
            ],
            "styles": [
              "node_modules/primeng/resources/primeng.min.css",
              "node_modules/@fullcalendar/core/main.css",
              "node_modules/@fullcalendar/daygrid/main.css",
              "node_modules/quill/dist/quill.snow.css",
              "node_modules/primeicons/primeicons.css",
              {
                "input": "src/assets/styles/variables.css"
              },
              "src/styles.scss",
              "src/assets/styles/main.scss"
            ],
            "scripts": [
              "node_modules/moment/moment.js",
              "node_modules/chart.js/dist/Chart.js",
              "node_modules/quill/dist/quill.js"
            ]
          },
          "configurations": {
            "production": {
              "budgets": [
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "6kb"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ]
            },
            "docker": {
              "budgets": [
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "6kb"
                }
              ],
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.docker.ts"
                }
              ]
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "voss-portal:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "voss-portal:build:production"
            },
            "docker": {
              "browserTarget": "voss-portal:build:docker",
              "ssl": true,
              "port": 4201,
              "servePath": "/portal/",
              "deployUrl": "/portal/",
              "baseHref": "/portal/"
            },
            "proxy": {
              "browserTarget": "voss-portal:build:docker",
              "proxyConfig": "proxy.conf.js",
              "ssl": true,
              "port": 4200,
              "servePath": "/portal/",
              "deployUrl": "/portal/",
              "baseHref": "/portal/"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "voss-portal:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.spec.json",
            "karmaConfig": "src/karma.conf.js",
            "scripts": [
              "node_modules/moment/moment.js",
              "node_modules/chart.js/dist/Chart.js",
              "node_modules/quill/dist/quill.js"
            ],
            "styles": [
              "node_modules/primeng/resources/primeng.min.css",
              "node_modules/quill/dist/quill.snow.css"
            ],
            "assets": [
              "src/assets",
              "src/upload.php",
              "src/favicon.ico"
            ],
            "fileReplacements": [
              {
                "replace": "src/environments/environment.ts",
                "with": "src/environments/environment.testenv.ts"
              }
            ]
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": [
              "src/tsconfig.app.json",
              "src/tsconfig.spec.json",
              "cypress/tsconfig.json"
            ],
            "exclude": [
              "**/node_modules/**"
            ]
          }
        },
        "cypress-run": {
          "builder": "@briebug/cypress-schematic:cypress",
          "options": {
            "devServerTarget": "voss-portal:serve"
          },
          "configurations": {
            "production": {
              "devServerTarget": "voss-portal:serve:production"
            }
          }
        },
        "cypress-open": {
          "builder": "@briebug/cypress-schematic:cypress",
          "options": {
            "devServerTarget": "voss-portal:serve",
            "watch": true,
            "headless": false
          },
          "configurations": {
            "production": {
              "devServerTarget": "voss-portal:serve:production"
            }
          }
        }
      }
    },
    "voss-portal-e2e": {
      "root": "",
      "sourceRoot": "e2e",
      "projectType": "application",
      "architect": {
        "e2e": {
          "builder": "@angular-devkit/build-angular:protractor",
          "options": {
            "protractorConfig": "e2e/protractor.conf.js",
            "devServerTarget": "voss-portal:serve"
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": [
              "e2e/tsconfig.e2e.json",
              "cypress/tsconfig.json"
            ],
            "exclude": [
              "**/node_modules/**"
            ]
          }
        },
        "cypress-run": {
          "builder": "@briebug/cypress-schematic:cypress",
          "options": {
            "devServerTarget": "voss-portal-e2e:serve"
          },
          "configurations": {
            "production": {
              "devServerTarget": "voss-portal-e2e:serve:production"
            }
          }
        },
        "cypress-open": {
          "builder": "@briebug/cypress-schematic:cypress",
          "options": {
            "devServerTarget": "voss-portal-e2e:serve",
            "watch": true,
            "headless": false
          },
          "configurations": {
            "production": {
              "devServerTarget": "voss-portal-e2e:serve:production"
            }
          }
        }
      }
    }
  },
  "defaultProject": "voss-portal",
  "schematics": {
    "@schematics/angular:component": {
      "prefix": "app",
      "style": "css"
    },
    "@schematics/angular:directive": {
      "prefix": "app"
    }
  },
  "cli": {
    "analytics": false
  }
}
LeJeanbono commented 4 years ago

So where are they included ? All imported in src/styles.scss ?

azaeng04 commented 4 years ago

@LeJeanbono that is correct. All in one css file. When compiling the angular app it creates a single styles.css file and all styles are in there. When I load the site directly it just references that style.css.

LeJeanbono commented 4 years ago

So, how do you compile them ?

azaeng04 commented 4 years ago

ng serve, but with unit testing we shouldn't need to do that.

azaeng04 commented 4 years ago

The styles are referenced directly in the test. Just to give you an idea of what the structure of the project looks like. I am not sure if the cypress webpack preprocessor is probably wrong, but it doesn't seem to load those assets based on the config:

Cypress webpack:

const webpackPreprocessor = require("@cypress/webpack-preprocessor");
const helpers = require('./helpers');
const webpack = require('webpack');
const path = require('path');

const webpackOptions = {
    mode: 'development',
    devtool: 'inline-source-map',
    resolve: {
        extensions: [".ts", ".js"],
        modules: [helpers.root('src'), 'node_modules'],
        alias: {
            src: path.resolve(__dirname, '../../src'),
            assets: path.resolve(__dirname, '../../src/assets'),
        }
    },
    module: {
        rules: [{
                enforce: 'pre',
                test: /\.js$/,
                loader: 'source-map-loader',
                exclude: [
                  helpers.root('node_modules/@angular')
                ]
            },
            {
                // Mark files inside `@angular/core` as using SystemJS style dynamic imports.
                // Removing this will cause deprecation warnings to appear.
                test: /[\/\\]@angular[\/\\]core[\/\\].+\.js$/,
                parser: {
                    system: true
                },
            },
            {
                test: /\.ts$/,
                use: [{
                    loader: 'ts-loader',
                    options: {
                        transpileOnly: true
                    }
                }, {
                    loader: 'angular2-template-loader'
                }],
                exclude: [/node_modules/],
            },
            {
                test: /\.(s?[ac]ss)$/,
                use: ['to-string-loader', 'css-loader', 'sass-loader'],
                exclude: [helpers.root('src/index.html')]
            },
            {
                test: /\.(html)$/,
                loader: 'html-loader',
                exclude: [helpers.root('src/index.html')]
            },
            {
                test: /\.(jpe?g|png|gif)$/i,
                loader: 'file-loader',
                options: {
                    name: 'assets/images/[name].[ext]'
                }
            },
            {
                test: /\.(mp4|webm|ogg)$/i,
                loader: 'file-loader',
                options: {
                    name: 'assets/videos/[name].[ext]'
                }
            },
            {
                test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
                loader: 'file-loader',
                options: {
                    name: 'assets/svgs/[name].[ext]',
                    limit: 10000,
                    mimetype: 'image/svg+xml'
                }
            },
            {
                test: /\.eot(\?v=\d+.\d+.\d+)?$/,
                loader: 'file-loader',
                options: {
                    prefix: 'font/',
                    limit: 5000,
                    name: 'assets/fonts/[name].[ext]'
                }
            },
            {
                test: /\.(woff|woff2)$/,
                loader: 'file-loader',
                options: {
                    prefix: 'font/',
                    limit: 5000,
                    name: 'assets/fonts/[name].[ext]'
                }
            },
            {
                test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
                loader: 'file-loader',
                options: {
                    limit: 10000,
                    mimetype: 'application/octet-stream',
                    name: 'assets/fonts/[name].[ext]'
                }
            },
        ]
    },
    plugins: [
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'test')
        }),
        new webpack.ContextReplacementPlugin(
            /\@angular(\\|\/)core(\\|\/)f?esm5/, path.join(__dirname, './src')
        ),
    ],
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist'),
    },
};

const options = {
    webpackOptions
};

module.exports = webpackPreprocessor(options);

An idea of the structure:

structure

What am I doing wrong?

LeJeanbono commented 4 years ago

Not a webpack problem. You're styles need to be imported directly in the test dom (This is what initConfig does with cssFile) I'm working to doing it automatically by reading angular.json styles option (Angular cli doing the same) I keep you in touch

azaeng04 commented 4 years ago

I see okay cool. Also it is not CSS files it is SCSS files. I look forward to that fix. I think it would be a lot better than having to import each and every style required for the component.

azaeng04 commented 4 years ago

@LeJeanbono I see your examples don't make use of SCSS. If I change a CSS file to SCSS it doesn't load the style on the component.

LeJeanbono commented 4 years ago

@azaeng04 See https://github.com/bahmutov/cypress-angular-unit-test/tree/master/examples/ng9/src/app/scss-style

I'm working on global style at this moment

azaeng04 commented 4 years ago

That's great. I have decided to go with Storybook + Jest + Puppeteer for the visual type of testing. Until Cypress is fully functional with Angular component testing.

logitimate commented 3 years ago

This sounds similar to what I'm experiencing. We have a design system and we have a global scss file that normally the app consumes. What is the best way to get this scss into the test dom. Seems like everything else is working though. Really cool tool!

azaeng04 commented 3 years ago

Yeah @logitimate I just kinda gave up in the meantime and started just focusing on getting proper unit/integration tests on Angular components. I would rather wait until Cypress fully supports it.