igniteui-angular 22.2.0-rc.0 → 22.2.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +1 -1
  2. package/button-group/README.md +42 -9
  3. package/calendar/README.md +30 -20
  4. package/card/README.md +1 -1
  5. package/fesm2022/igniteui-angular-accordion.mjs +7 -7
  6. package/fesm2022/igniteui-angular-action-strip.mjs +11 -22
  7. package/fesm2022/igniteui-angular-action-strip.mjs.map +1 -1
  8. package/fesm2022/igniteui-angular-avatar.mjs +7 -7
  9. package/fesm2022/igniteui-angular-badge.mjs +7 -7
  10. package/fesm2022/igniteui-angular-banner.mjs +10 -10
  11. package/fesm2022/igniteui-angular-bottom-nav.mjs +22 -22
  12. package/fesm2022/igniteui-angular-button-group.mjs +32 -35
  13. package/fesm2022/igniteui-angular-button-group.mjs.map +1 -1
  14. package/fesm2022/igniteui-angular-calendar.mjs +132 -180
  15. package/fesm2022/igniteui-angular-calendar.mjs.map +1 -1
  16. package/fesm2022/igniteui-angular-card.mjs +47 -58
  17. package/fesm2022/igniteui-angular-card.mjs.map +1 -1
  18. package/fesm2022/igniteui-angular-carousel.mjs +22 -22
  19. package/fesm2022/igniteui-angular-chat-extras.mjs +6 -6
  20. package/fesm2022/igniteui-angular-chat.mjs +12 -12
  21. package/fesm2022/igniteui-angular-checkbox.mjs +7 -7
  22. package/fesm2022/igniteui-angular-chips.mjs +10 -10
  23. package/fesm2022/igniteui-angular-combo.mjs +69 -67
  24. package/fesm2022/igniteui-angular-combo.mjs.map +1 -1
  25. package/fesm2022/igniteui-angular-core.mjs +139 -92
  26. package/fesm2022/igniteui-angular-core.mjs.map +1 -1
  27. package/fesm2022/igniteui-angular-date-picker.mjs +53 -70
  28. package/fesm2022/igniteui-angular-date-picker.mjs.map +1 -1
  29. package/fesm2022/igniteui-angular-dialog.mjs +13 -13
  30. package/fesm2022/igniteui-angular-directives.mjs +194 -194
  31. package/fesm2022/igniteui-angular-drop-down.mjs +29 -29
  32. package/fesm2022/igniteui-angular-expansion-panel.mjs +28 -28
  33. package/fesm2022/igniteui-angular-grids-core.mjs +737 -661
  34. package/fesm2022/igniteui-angular-grids-core.mjs.map +1 -1
  35. package/fesm2022/igniteui-angular-grids-grid.mjs +49 -49
  36. package/fesm2022/igniteui-angular-grids-hierarchical-grid.mjs +37 -37
  37. package/fesm2022/igniteui-angular-grids-lite.mjs +25 -17
  38. package/fesm2022/igniteui-angular-grids-lite.mjs.map +1 -1
  39. package/fesm2022/igniteui-angular-grids-pivot-grid.mjs +80 -80
  40. package/fesm2022/igniteui-angular-grids-pivot-grid.mjs.map +1 -1
  41. package/fesm2022/igniteui-angular-grids-tree-grid.mjs +55 -55
  42. package/fesm2022/igniteui-angular-icon.mjs +10 -10
  43. package/fesm2022/igniteui-angular-input-group.mjs +59 -55
  44. package/fesm2022/igniteui-angular-input-group.mjs.map +1 -1
  45. package/fesm2022/igniteui-angular-list.mjs +40 -40
  46. package/fesm2022/igniteui-angular-navbar.mjs +13 -13
  47. package/fesm2022/igniteui-angular-navigation-drawer.mjs +43 -38
  48. package/fesm2022/igniteui-angular-navigation-drawer.mjs.map +1 -1
  49. package/fesm2022/igniteui-angular-paginator.mjs +19 -19
  50. package/fesm2022/igniteui-angular-progressbar.mjs +19 -19
  51. package/fesm2022/igniteui-angular-query-builder.mjs +22 -22
  52. package/fesm2022/igniteui-angular-radio.mjs +25 -21
  53. package/fesm2022/igniteui-angular-radio.mjs.map +1 -1
  54. package/fesm2022/igniteui-angular-select.mjs +29 -33
  55. package/fesm2022/igniteui-angular-select.mjs.map +1 -1
  56. package/fesm2022/igniteui-angular-simple-combo.mjs +9 -9
  57. package/fesm2022/igniteui-angular-simple-combo.mjs.map +1 -1
  58. package/fesm2022/igniteui-angular-slider.mjs +28 -28
  59. package/fesm2022/igniteui-angular-snackbar.mjs +7 -7
  60. package/fesm2022/igniteui-angular-splitter.mjs +13 -13
  61. package/fesm2022/igniteui-angular-stepper.mjs +34 -34
  62. package/fesm2022/igniteui-angular-switch.mjs +7 -7
  63. package/fesm2022/igniteui-angular-tabs.mjs +34 -34
  64. package/fesm2022/igniteui-angular-time-picker.mjs +26 -36
  65. package/fesm2022/igniteui-angular-time-picker.mjs.map +1 -1
  66. package/fesm2022/igniteui-angular-toast.mjs +7 -7
  67. package/fesm2022/igniteui-angular-tree.mjs +28 -28
  68. package/fesm2022/igniteui-angular-virtual-scroll.mjs +497 -145
  69. package/fesm2022/igniteui-angular-virtual-scroll.mjs.map +1 -1
  70. package/migrations/common/UpdateChanges.d.ts +56 -0
  71. package/migrations/common/UpdateChanges.js +366 -38
  72. package/migrations/common/UpdateChanges.spec.js +829 -0
  73. package/migrations/migration-collection.json +1 -1
  74. package/migrations/update-22_2_0/index.js +145 -0
  75. package/migrations/update-22_2_0/index.spec.js +206 -0
  76. package/navigation-drawer/README.md +1 -1
  77. package/package.json +3 -3
  78. package/schematics/tsconfig.tsbuildinfo +1 -1
  79. package/skills/igniteui-angular-components/SKILL.md +9 -5
  80. package/skills/igniteui-angular-components/references/form-controls.md +1 -1
  81. package/skills/igniteui-angular-components/references/mcp-setup.md +14 -2
  82. package/skills/igniteui-angular-figma-to-app/SKILL.md +112 -525
  83. package/skills/igniteui-angular-figma-to-app/references/asset-extraction.md +49 -75
  84. package/skills/igniteui-angular-figma-to-app/references/design-provenance.md +201 -0
  85. package/skills/igniteui-angular-figma-to-app/references/design-token-bridge.md +175 -52
  86. package/skills/igniteui-angular-figma-to-app/references/figma-component-map.md +153 -99
  87. package/skills/igniteui-angular-figma-to-app/references/figma-exploration.md +226 -0
  88. package/skills/igniteui-angular-figma-to-app/references/mcp-setup.md +71 -105
  89. package/skills/igniteui-angular-figma-to-app/references/project-setup.md +63 -0
  90. package/skills/igniteui-angular-figma-to-app/references/theme-generation.md +120 -0
  91. package/skills/igniteui-angular-figma-to-app/references/validation-patterns.md +48 -50
  92. package/skills/igniteui-angular-generate-from-image-design/SKILL.md +11 -7
  93. package/skills/igniteui-angular-grids/SKILL.md +7 -3
  94. package/skills/igniteui-angular-grids/references/editing.md +1 -2
  95. package/skills/igniteui-angular-grids/references/grid-migration.md +1 -1
  96. package/skills/igniteui-angular-theming/SKILL.md +9 -5
  97. package/skills/igniteui-angular-theming/references/mcp-setup.md +12 -2
  98. package/types/igniteui-angular-button-group.d.ts +49 -34
  99. package/types/igniteui-angular-calendar.d.ts +33 -50
  100. package/types/igniteui-angular-card.d.ts +12 -17
  101. package/types/igniteui-angular-combo.d.ts +6 -0
  102. package/types/igniteui-angular-core.d.ts +25 -8
  103. package/types/igniteui-angular-grids-core.d.ts +48 -4
  104. package/types/igniteui-angular-grids-lite.d.ts +5 -1
  105. package/types/igniteui-angular-grids-pivot-grid.d.ts +1 -1
  106. package/types/igniteui-angular-input-group.d.ts +17 -3
  107. package/types/igniteui-angular-navigation-drawer.d.ts +1 -0
  108. package/types/igniteui-angular-radio.d.ts +5 -0
  109. package/types/igniteui-angular-time-picker.d.ts +0 -1
  110. package/types/igniteui-angular-virtual-scroll.d.ts +41 -13
  111. package/virtual-scroll/README.md +32 -1
@@ -567,6 +567,835 @@ $var3: igx-comp-theme(
567
567
  expect(appTree.readContent('test.component.scss')).toEqual(`igx-theme-func($replaced: 10px, $old-prop: 3, $prop3: 2);`);
568
568
  done();
569
569
  });
570
+ it('should keep the brackets balanced for nested theme functions', done => {
571
+ const themeChangesJson = {
572
+ changes: [
573
+ {
574
+ name: '$remove-me', remove: true,
575
+ owner: 'igx-theme-func',
576
+ type: schema_1.ThemeType.Property
577
+ },
578
+ {
579
+ name: '$replace-me', replaceWith: '$replaced',
580
+ owner: 'igx-theme-func',
581
+ type: schema_1.ThemeType.Property
582
+ }
583
+ ]
584
+ };
585
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
586
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
587
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
588
+ appTree.create('styles.scss', `@include igx-mixin(igx-theme-func($remove-me: 6px));
589
+ @include igx-mixin(igx-theme-func($prop1: red, $remove-me: 6px));
590
+ @include igx-mixin(igx-theme-func($remove-me: 6px, $prop1: red));
591
+ @include igx-mixin(igx-theme-func($replace-me: 6px));
592
+ $var: igx-theme-func($content: "not a ( bracket", $remove-me: 6px);
593
+ $var2: igx-theme-func($image: url(https://example.com/a.png), $remove-me: 6px);
594
+ $var3: igx-theme-func(
595
+ $remove-me: 6px, // not a ) bracket
596
+ $prop1: red
597
+ );`);
598
+ const update = new UnitUpdateChanges(__dirname, appTree);
599
+ update.applyChanges();
600
+ expect(appTree.readContent('styles.scss')).toEqual(`@include igx-mixin(igx-theme-func());
601
+ @include igx-mixin(igx-theme-func($prop1: red));
602
+ @include igx-mixin(igx-theme-func( $prop1: red));
603
+ @include igx-mixin(igx-theme-func($replaced: 6px));
604
+ $var: igx-theme-func($content: "not a ( bracket");
605
+ $var2: igx-theme-func($image: url(https://example.com/a.png));
606
+ $var3: igx-theme-func( // not a ) bracket
607
+ $prop1: red
608
+ );`);
609
+ done();
610
+ });
611
+ it('should not treat a theme function mentioned in a comment or a string as a call', done => {
612
+ const themeChangesJson = {
613
+ changes: [
614
+ {
615
+ name: '$remove-me', remove: true,
616
+ owner: 'igx-theme-func',
617
+ type: schema_1.ThemeType.Property
618
+ }
619
+ ]
620
+ };
621
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
622
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
623
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
624
+ appTree.create('styles.scss', `// igx-theme-func($remove-me: 1px) is gone, use the grid's own borders
625
+ /* igx-theme-func($remove-me: 2px */
626
+ $doc: "igx-theme-func($remove-me: 3px";
627
+ $var: igx-theme-func($remove-me: 4px, $prop1: red);`);
628
+ const update = new UnitUpdateChanges(__dirname, appTree);
629
+ update.applyChanges();
630
+ expect(appTree.readContent('styles.scss')).toEqual(`// igx-theme-func($remove-me: 1px) is gone, use the grid's own borders
631
+ /* igx-theme-func($remove-me: 2px */
632
+ $doc: "igx-theme-func($remove-me: 3px";
633
+ $var: igx-theme-func( $prop1: red);`);
634
+ done();
635
+ });
636
+ it('should remove a theme property that has a comment in front of it and keep the comment', done => {
637
+ const themeChangesJson = {
638
+ changes: [
639
+ {
640
+ name: '$remove-me', remove: true,
641
+ owner: 'igx-theme-func',
642
+ type: schema_1.ThemeType.Property
643
+ }
644
+ ]
645
+ };
646
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
647
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
648
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
649
+ appTree.create('styles.scss', `$var1: igx-theme-func(
650
+ $prop1: red, // brand color
651
+ $remove-me: 6px
652
+ );
653
+ $var2: igx-theme-func(
654
+ $prop1: red, // brand color
655
+ $remove-me: 6px,
656
+ $prop2: blue
657
+ );
658
+ $var3: igx-theme-func($prop1: red, /* legacy */ $remove-me: 6px);
659
+ $var4: igx-theme-func($prop1: red, // legacy
660
+ $remove-me: 6px, $prop2: blue);
661
+ $var5: igx-theme-func($prop1: red, // legacy
662
+ $remove-me: 6px);`);
663
+ const update = new UnitUpdateChanges(__dirname, appTree);
664
+ update.applyChanges();
665
+ expect(appTree.readContent('styles.scss')).toEqual(`$var1: igx-theme-func(
666
+ $prop1: red // brand color
667
+ );
668
+ $var2: igx-theme-func(
669
+ $prop1: red, // brand color
670
+ $prop2: blue
671
+ );
672
+ $var3: igx-theme-func($prop1: red /* legacy */);
673
+ $var4: igx-theme-func($prop1: red, // legacy
674
+ $prop2: blue);
675
+ $var5: igx-theme-func($prop1: red // legacy
676
+ );`);
677
+ done();
678
+ });
679
+ it('should update a theme property that has whitespace before its colon', done => {
680
+ const themeChangesJson = {
681
+ changes: [
682
+ {
683
+ name: '$remove-me', remove: true,
684
+ owner: 'igx-theme-func',
685
+ type: schema_1.ThemeType.Property
686
+ },
687
+ {
688
+ name: '$replace-me', replaceWith: '$replaced',
689
+ owner: 'igx-theme-func',
690
+ type: schema_1.ThemeType.Property
691
+ }
692
+ ]
693
+ };
694
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
695
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
696
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
697
+ appTree.create('styles.scss', `$var1: igx-theme-func($prop1 : red, $remove-me : 6px);
698
+ $var2: igx-theme-func(
699
+ $prop1 : red,
700
+ $remove-me : 6px
701
+ );
702
+ $var3: igx-theme-func($replace-me : 6px);`);
703
+ const update = new UnitUpdateChanges(__dirname, appTree);
704
+ update.applyChanges();
705
+ expect(appTree.readContent('styles.scss')).toEqual(`$var1: igx-theme-func($prop1 : red);
706
+ $var2: igx-theme-func(
707
+ $prop1 : red
708
+ );
709
+ $var3: igx-theme-func($replaced : 6px);`);
710
+ done();
711
+ });
712
+ it('should not update a function or mixin the stylesheet declares itself', done => {
713
+ const themeChangesJson = {
714
+ changes: [
715
+ {
716
+ name: '$remove-me', remove: true,
717
+ owner: 'igx-theme-func',
718
+ type: schema_1.ThemeType.Property
719
+ }
720
+ ]
721
+ };
722
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
723
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
724
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
725
+ const userCode = `@use "igniteui-angular/theming" as igx;
726
+ @mixin app-igx-theme-func($remove-me: 1px) {
727
+ border-width: $remove-me;
728
+ }
729
+ @include app-igx-theme-func($remove-me: 2px);
730
+ @function my_igx-theme-func($remove-me: 1px) {
731
+ @return $remove-me;
732
+ }
733
+ $var1: my_igx-theme-func($remove-me: 2px);
734
+ `;
735
+ appTree.create('styles.scss', userCode + `$var2: igx.igx-theme-func($remove-me: 2px, $prop1: red);`);
736
+ const update = new UnitUpdateChanges(__dirname, appTree);
737
+ update.applyChanges();
738
+ // a namespaced call to the library function is still migrated
739
+ expect(appTree.readContent('styles.scss')).toEqual(userCode + `$var2: igx.igx-theme-func( $prop1: red);`);
740
+ done();
741
+ });
742
+ it('should not take a declaration in a comment or a string for one the stylesheet makes', done => {
743
+ const themeChangesJson = {
744
+ changes: [
745
+ {
746
+ name: '$remove-me', remove: true,
747
+ owner: 'igx-theme-func',
748
+ type: schema_1.ThemeType.Property
749
+ }
750
+ ]
751
+ };
752
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
753
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
754
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
755
+ appTree.create('styles.scss', `// @function igx-theme-func($remove-me: 1px) {}
756
+ $var1: igx-theme-func($prop1: red, $remove-me: 6px);
757
+ $doc: "@mixin igx-theme-func($remove-me: 1px)";
758
+ $var2: igx-theme-func($remove-me: 6px);`);
759
+ const update = new UnitUpdateChanges(__dirname, appTree);
760
+ update.applyChanges();
761
+ expect(appTree.readContent('styles.scss')).toEqual(`// @function igx-theme-func($remove-me: 1px) {}
762
+ $var1: igx-theme-func($prop1: red);
763
+ $doc: "@mixin igx-theme-func($remove-me: 1px)";
764
+ $var2: igx-theme-func();`);
765
+ done();
766
+ });
767
+ it('should update a namespaced Ignite UI call although the stylesheet declares the same name', done => {
768
+ const themeChangesJson = {
769
+ changes: [
770
+ {
771
+ name: '$remove-me', remove: true,
772
+ owner: 'igx-theme-func',
773
+ type: schema_1.ThemeType.Property
774
+ }
775
+ ]
776
+ };
777
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
778
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
779
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
780
+ // the two are separate members under Sass's namespace rules
781
+ appTree.create('styles.scss', `@use "igniteui-angular/theming" as igx;
782
+ @mixin igx-theme-func($remove-me: 1px) {
783
+ border-width: $remove-me;
784
+ }
785
+ @include igx-theme-func($remove-me: 2px);
786
+ $var: igx.igx-theme-func($remove-me: 6px);`);
787
+ const update = new UnitUpdateChanges(__dirname, appTree);
788
+ update.applyChanges();
789
+ expect(appTree.readContent('styles.scss')).toEqual(`@use "igniteui-angular/theming" as igx;
790
+ @mixin igx-theme-func($remove-me: 1px) {
791
+ border-width: $remove-me;
792
+ }
793
+ @include igx-theme-func($remove-me: 2px);
794
+ $var: igx.igx-theme-func();`);
795
+ done();
796
+ });
797
+ it('should tell a mixin the stylesheet declares from a function of the same name', done => {
798
+ const themeChangesJson = {
799
+ changes: [
800
+ {
801
+ name: '$remove-me', remove: true,
802
+ owner: 'igx-theme-func',
803
+ type: schema_1.ThemeType.Property
804
+ }
805
+ ]
806
+ };
807
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
808
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
809
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
810
+ // the mixin does not shadow the library's function, and the function does not shadow a mixin
811
+ appTree.create('styles.scss', `@mixin igx-theme-func($remove-me: 1px) {
812
+ border-width: $remove-me;
813
+ }
814
+ @function app-igx-theme-func($remove-me: 1px) {
815
+ @return $remove-me;
816
+ }
817
+ @include igx-theme-func($remove-me: 2px);
818
+ $var1: igx-theme-func($remove-me: 6px);
819
+ $var2: app-igx-theme-func($remove-me: 6px);
820
+ @include app-igx-theme-func($remove-me: 2px);`);
821
+ const update = new UnitUpdateChanges(__dirname, appTree);
822
+ update.applyChanges();
823
+ expect(appTree.readContent('styles.scss')).toEqual(`@mixin igx-theme-func($remove-me: 1px) {
824
+ border-width: $remove-me;
825
+ }
826
+ @function app-igx-theme-func($remove-me: 1px) {
827
+ @return $remove-me;
828
+ }
829
+ @include igx-theme-func($remove-me: 2px);
830
+ $var1: igx-theme-func();
831
+ $var2: app-igx-theme-func($remove-me: 6px);
832
+ @include app-igx-theme-func();`);
833
+ done();
834
+ });
835
+ it('should recognize equivalent underscore and hyphen names for local Sass members', () => {
836
+ const themeChangesJson = {
837
+ changes: [
838
+ { name: '$remove-me', remove: true, owner: 'theme', type: schema_1.ThemeType.Property }
839
+ ]
840
+ };
841
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
842
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
843
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
844
+ appTree.create('styles.scss', `@mixin app_theme($remove-me: 1px) { border-width: $remove-me; }
845
+ @mixin reverse-theme($remove-me: 1px) { border-width: $remove-me; }
846
+ @function custom_theme($remove-me: 1px) { @return $remove-me; }
847
+ @function reverse-function-theme($remove-me: 1px) { @return $remove-me; }
848
+ .calls {
849
+ @include app-theme($remove-me: 2px);
850
+ @include reverse_theme($remove-me: 2px);
851
+ $local: custom-theme($remove-me: 2px);
852
+ $reverse: reverse_function_theme($remove-me: 2px);
853
+ $library: app-theme($remove-me: 2px);
854
+ @include custom-theme($remove-me: 2px);
855
+ @include igx.app-theme($remove-me: 2px);
856
+ }`);
857
+ new UnitUpdateChanges(__dirname, appTree).applyChanges();
858
+ expect(appTree.readContent('styles.scss')).toEqual(`@mixin app_theme($remove-me: 1px) { border-width: $remove-me; }
859
+ @mixin reverse-theme($remove-me: 1px) { border-width: $remove-me; }
860
+ @function custom_theme($remove-me: 1px) { @return $remove-me; }
861
+ @function reverse-function-theme($remove-me: 1px) { @return $remove-me; }
862
+ .calls {
863
+ @include app-theme($remove-me: 2px);
864
+ @include reverse_theme($remove-me: 2px);
865
+ $local: custom-theme($remove-me: 2px);
866
+ $reverse: reverse_function_theme($remove-me: 2px);
867
+ $library: app-theme();
868
+ @include custom-theme();
869
+ @include igx.app-theme();
870
+ }`);
871
+ });
872
+ it('should recognize indented Sass shorthand declarations and includes within their scope', () => {
873
+ const themeChangesJson = {
874
+ changes: [
875
+ { name: '$remove-me', remove: true, owner: 'theme', type: schema_1.ThemeType.Property }
876
+ ]
877
+ };
878
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
879
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
880
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
881
+ const source = `= app-grid-summary-theme($remove-me: 1px)
882
+ border-width: $remove-me
883
+ @mixin long_theme($remove-me: 1px)
884
+ border-width: $remove-me
885
+ @function function-theme($remove-me: 1px)
886
+ @return $remove-me
887
+ .calls
888
+ +app-grid-summary-theme($remove-me: 2px)
889
+ @include app-grid-summary-theme($remove-me: 2px)
890
+ +long-theme($remove-me: 2px)
891
+ +function-theme($remove-me: 2px)
892
+ +igx.app-grid-summary-theme($remove-me: 2px)
893
+ $library: app-grid-summary-theme($remove-me: 2px)
894
+ $local: function-theme($remove-me: 2px)
895
+ @media screen
896
+ =scoped-theme($remove-me: 1px)
897
+ border-width: $remove-me
898
+ .local
899
+ +scoped-theme($remove-me: 2px)
900
+ .outside
901
+ +scoped-theme($remove-me: 2px)`;
902
+ const expected = `= app-grid-summary-theme($remove-me: 1px)
903
+ border-width: $remove-me
904
+ @mixin long_theme($remove-me: 1px)
905
+ border-width: $remove-me
906
+ @function function-theme($remove-me: 1px)
907
+ @return $remove-me
908
+ .calls
909
+ +app-grid-summary-theme($remove-me: 2px)
910
+ @include app-grid-summary-theme($remove-me: 2px)
911
+ +long-theme($remove-me: 2px)
912
+ +function-theme()
913
+ +igx.app-grid-summary-theme()
914
+ $library: app-grid-summary-theme()
915
+ $local: function-theme($remove-me: 2px)
916
+ @media screen
917
+ =scoped-theme($remove-me: 1px)
918
+ border-width: $remove-me
919
+ .local
920
+ +scoped-theme($remove-me: 2px)
921
+ .outside
922
+ +scoped-theme()`;
923
+ appTree.create('styles.sass', source);
924
+ appTree.create('windows.sass', source.replace(/\n/g, '\r\n'));
925
+ new UnitUpdateChanges(__dirname, appTree).applyChanges();
926
+ expect(appTree.readContent('styles.sass')).toEqual(expected);
927
+ expect(appTree.readContent('windows.sass')).toEqual(expected.replace(/\n/g, '\r\n'));
928
+ });
929
+ it('should distinguish indented Sass shorthand from operators in function expressions', () => {
930
+ const themeChangesJson = {
931
+ changes: [
932
+ { name: '$remove-me', remove: true, owner: 'theme', type: schema_1.ThemeType.Property }
933
+ ]
934
+ };
935
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
936
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
937
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
938
+ appTree.create('styles.sass', `=app-theme($remove-me: 1px)
939
+ border-width: $remove-me
940
+ @function function-theme($remove-me: 1px)
941
+ @return $remove-me
942
+ $direct: +app-theme($remove-me: 2px)
943
+ $equal: 1px == app-theme($remove-me: 2px)
944
+ $list: (
945
+ +app-theme($remove-me: 2px),
946
+ +function-theme($remove-me: 2px)
947
+ )
948
+ .calls
949
+ +app-theme($remove-me: 2px)
950
+ +function-theme($remove-me: 2px)`);
951
+ new UnitUpdateChanges(__dirname, appTree).applyChanges();
952
+ expect(appTree.readContent('styles.sass')).toEqual(`=app-theme($remove-me: 1px)
953
+ border-width: $remove-me
954
+ @function function-theme($remove-me: 1px)
955
+ @return $remove-me
956
+ $direct: +app-theme()
957
+ $equal: 1px == app-theme()
958
+ $list: (
959
+ +app-theme(),
960
+ +function-theme($remove-me: 2px)
961
+ )
962
+ .calls
963
+ +app-theme($remove-me: 2px)
964
+ +function-theme()`);
965
+ });
966
+ it('should classify mixin calls through comments without changing the comments', () => {
967
+ const themeChangesJson = {
968
+ changes: [
969
+ { name: '$remove-me', remove: true, owner: 'theme', type: schema_1.ThemeType.Property }
970
+ ]
971
+ };
972
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
973
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
974
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
975
+ const source = `@function theme($remove-me: false) { @return $remove-me; }
976
+ @mixin app-theme($remove-me: 1px) { border-width: $remove-me; }
977
+ @include /* brand */ theme($remove-me: true);
978
+ @include // brand
979
+ theme($remove-me: true);
980
+ @include /* keep */ app-theme($remove-me: 2px);
981
+ @include // keep
982
+ app-theme($remove-me: 2px);
983
+ $value: theme($remove-me: true);`;
984
+ const expected = `@function theme($remove-me: false) { @return $remove-me; }
985
+ @mixin app-theme($remove-me: 1px) { border-width: $remove-me; }
986
+ @include /* brand */ theme();
987
+ @include // brand
988
+ theme();
989
+ @include /* keep */ app-theme($remove-me: 2px);
990
+ @include // keep
991
+ app-theme($remove-me: 2px);
992
+ $value: theme($remove-me: true);`;
993
+ for (const eol of ['\n', '\r\n']) {
994
+ const file = eol === '\n' ? 'styles.scss' : 'windows.scss';
995
+ appTree.create(file, source.replace(/\n/g, eol));
996
+ }
997
+ new UnitUpdateChanges(__dirname, appTree).applyChanges();
998
+ expect(appTree.readContent('styles.scss')).toEqual(expected);
999
+ expect(appTree.readContent('windows.scss')).toEqual(expected.replace(/\n/g, '\r\n'));
1000
+ });
1001
+ it('should migrate immediate calls before local Sass declarations become available', () => {
1002
+ const themeChangesJson = {
1003
+ changes: [
1004
+ { name: '$border-width', remove: true, owner: 'grid-summary-theme', type: schema_1.ThemeType.Property }
1005
+ ]
1006
+ };
1007
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
1008
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
1009
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
1010
+ appTree.create('styles.scss', `$before: grid-summary-theme($border-width: 2px);
1011
+ .before { @include grid-summary-theme($border-width: 2px); }
1012
+ @function grid_summary_theme($border-width: 1px) { @return $border-width; }
1013
+ @mixin grid-summary-theme($border-width: 1px) { border-width: $border-width; }
1014
+ $after: grid-summary-theme($border-width: 2px);
1015
+ .after { @include grid-summary-theme($border-width: 2px); }
1016
+ @function grid-summary-theme($border-width: 3px) { @return $border-width; }
1017
+ @mixin grid_summary_theme($border-width: 3px) { border-width: $border-width; }
1018
+ @media screen {
1019
+ $before: app-grid-summary-theme($border-width: 2px);
1020
+ @function app-grid-summary-theme($border-width: 1px) { @return $border-width; }
1021
+ $after: app-grid-summary-theme($border-width: 2px);
1022
+ }
1023
+ $outside: app-grid-summary-theme($border-width: 2px);`);
1024
+ new UnitUpdateChanges(__dirname, appTree).applyChanges();
1025
+ expect(appTree.readContent('styles.scss')).toEqual(`$before: grid-summary-theme();
1026
+ .before { @include grid-summary-theme(); }
1027
+ @function grid_summary_theme($border-width: 1px) { @return $border-width; }
1028
+ @mixin grid-summary-theme($border-width: 1px) { border-width: $border-width; }
1029
+ $after: grid-summary-theme($border-width: 2px);
1030
+ .after { @include grid-summary-theme($border-width: 2px); }
1031
+ @function grid-summary-theme($border-width: 3px) { @return $border-width; }
1032
+ @mixin grid_summary_theme($border-width: 3px) { border-width: $border-width; }
1033
+ @media screen {
1034
+ $before: app-grid-summary-theme();
1035
+ @function app-grid-summary-theme($border-width: 1px) { @return $border-width; }
1036
+ $after: app-grid-summary-theme($border-width: 2px);
1037
+ }
1038
+ $outside: app-grid-summary-theme();`);
1039
+ });
1040
+ it('should respect declaration order for immediate indented Sass calls', () => {
1041
+ const themeChangesJson = {
1042
+ changes: [
1043
+ { name: '$border-width', remove: true, owner: 'grid-summary-theme', type: schema_1.ThemeType.Property }
1044
+ ]
1045
+ };
1046
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
1047
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
1048
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
1049
+ const source = `$before: grid-summary-theme($border-width: 2px)
1050
+ .before
1051
+ +grid-summary-theme($border-width: 2px)
1052
+ @function grid_summary_theme($border-width: 1px)
1053
+ @return $border-width
1054
+ =grid-summary-theme($border-width: 1px)
1055
+ border-width: $border-width
1056
+ $after: grid-summary-theme($border-width: 2px)
1057
+ .after
1058
+ @include grid-summary-theme($border-width: 2px)
1059
+ @media screen
1060
+ $before: app-grid-summary-theme($border-width: 2px)
1061
+ @function app-grid-summary-theme($border-width: 1px)
1062
+ @return $border-width
1063
+ $after: app-grid-summary-theme($border-width: 2px)
1064
+ $outside: app-grid-summary-theme($border-width: 2px)`;
1065
+ const expected = `$before: grid-summary-theme()
1066
+ .before
1067
+ +grid-summary-theme()
1068
+ @function grid_summary_theme($border-width: 1px)
1069
+ @return $border-width
1070
+ =grid-summary-theme($border-width: 1px)
1071
+ border-width: $border-width
1072
+ $after: grid-summary-theme($border-width: 2px)
1073
+ .after
1074
+ @include grid-summary-theme($border-width: 2px)
1075
+ @media screen
1076
+ $before: app-grid-summary-theme()
1077
+ @function app-grid-summary-theme($border-width: 1px)
1078
+ @return $border-width
1079
+ $after: app-grid-summary-theme($border-width: 2px)
1080
+ $outside: app-grid-summary-theme()`;
1081
+ appTree.create('styles.sass', source);
1082
+ appTree.create('windows.sass', source.replace(/\n/g, '\r\n'));
1083
+ new UnitUpdateChanges(__dirname, appTree).applyChanges();
1084
+ expect(appTree.readContent('styles.sass')).toEqual(expected);
1085
+ expect(appTree.readContent('windows.sass')).toEqual(expected.replace(/\n/g, '\r\n'));
1086
+ });
1087
+ it('should preserve forward references in Sass bodies and default arguments evaluated later', () => {
1088
+ const themeChangesJson = {
1089
+ changes: [
1090
+ { name: '$border-width', remove: true, owner: 'grid-summary-theme', type: schema_1.ThemeType.Property }
1091
+ ]
1092
+ };
1093
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
1094
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
1095
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
1096
+ const scss = `@function wrapper($value: grid-summary-theme($border-width: 2px)) {
1097
+ @return grid-summary-theme($border-width: $value);
1098
+ }
1099
+ @mixin wrapper($value: grid-summary-theme($border-width: 2px)) {
1100
+ @include grid-summary-theme($border-width: $value);
1101
+ }
1102
+ @function grid-summary-theme($border-width: 1px) { @return $border-width; }
1103
+ @mixin grid-summary-theme($border-width: 1px) { border-width: $border-width; }
1104
+ .calls { @include wrapper(); width: wrapper(); }`;
1105
+ const sass = `@function wrapper($value: grid-summary-theme($border-width: 2px))
1106
+ @return grid-summary-theme($border-width: $value)
1107
+ =wrapper($value: grid-summary-theme($border-width: 2px))
1108
+ +grid-summary-theme($border-width: $value)
1109
+ =empty($value: grid-summary-theme($border-width: 2px))
1110
+ @function grid-summary-theme($border-width: 1px)
1111
+ @return $border-width
1112
+ =grid-summary-theme($border-width: 1px)
1113
+ border-width: $border-width
1114
+ .calls
1115
+ +wrapper()
1116
+ +empty()
1117
+ width: wrapper()`;
1118
+ appTree.create('styles.scss', scss);
1119
+ appTree.create('styles.sass', sass);
1120
+ new UnitUpdateChanges(__dirname, appTree).applyChanges();
1121
+ expect(appTree.readContent('styles.scss')).toEqual(scss);
1122
+ expect(appTree.readContent('styles.sass')).toEqual(sass);
1123
+ });
1124
+ it('should limit local theme declarations to their enclosing Sass block', () => {
1125
+ const themeChangesJson = {
1126
+ changes: [
1127
+ { name: '$remove-me', remove: true, owner: 'theme', type: schema_1.ThemeType.Property },
1128
+ { name: '$old', replaceWith: '$new', owner: 'theme', type: schema_1.ThemeType.Property }
1129
+ ]
1130
+ };
1131
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
1132
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
1133
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
1134
+ appTree.create('styles.scss', `$before: theme($remove-me: 2px, $old: red);
1135
+ @media screen {
1136
+ /* } @function theme() { */
1137
+ $text: "}";
1138
+ @function theme($remove-me: 1px, $old: red) { @return $old; }
1139
+ @mixin theme($remove-me: 1px, $old: red) { color: $old; }
1140
+ $local: theme($remove-me: 2px, $old: red);
1141
+ .nested { @include theme($remove-me: 2px, $old: red); }
1142
+ }
1143
+ @media print {
1144
+ $sibling: theme($remove-me: 2px, $old: red);
1145
+ @include theme($remove-me: 2px, $old: red);
1146
+ }
1147
+ $after: theme($remove-me: 2px, $old: red);`);
1148
+ new UnitUpdateChanges(__dirname, appTree).applyChanges();
1149
+ expect(appTree.readContent('styles.scss')).toEqual(`$before: theme( $new: red);
1150
+ @media screen {
1151
+ /* } @function theme() { */
1152
+ $text: "}";
1153
+ @function theme($remove-me: 1px, $old: red) { @return $old; }
1154
+ @mixin theme($remove-me: 1px, $old: red) { color: $old; }
1155
+ $local: theme($remove-me: 2px, $old: red);
1156
+ .nested { @include theme($remove-me: 2px, $old: red); }
1157
+ }
1158
+ @media print {
1159
+ $sibling: theme( $new: red);
1160
+ @include theme( $new: red);
1161
+ }
1162
+ $after: theme( $new: red);`);
1163
+ });
1164
+ it('should limit local theme declarations to their enclosing indented Sass block', () => {
1165
+ const themeChangesJson = {
1166
+ changes: [
1167
+ { name: '$remove-me', remove: true, owner: 'theme', type: schema_1.ThemeType.Property }
1168
+ ]
1169
+ };
1170
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
1171
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
1172
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
1173
+ appTree.create('styles.sass', `@media screen
1174
+ /* local function */
1175
+ @function theme($remove-me: 1px)
1176
+ @return red
1177
+ @mixin theme($remove-me: 1px)
1178
+ color: red
1179
+ $local: theme($remove-me: 2px)
1180
+ .nested
1181
+ @include theme($remove-me: 2px)
1182
+ @media print
1183
+ $sibling: theme($remove-me: 2px)
1184
+ $after: theme(
1185
+ $remove-me: 2px
1186
+ )
1187
+ @include theme($remove-me: 2px)`);
1188
+ new UnitUpdateChanges(__dirname, appTree).applyChanges();
1189
+ expect(appTree.readContent('styles.sass')).toEqual(`@media screen
1190
+ /* local function */
1191
+ @function theme($remove-me: 1px)
1192
+ @return red
1193
+ @mixin theme($remove-me: 1px)
1194
+ color: red
1195
+ $local: theme($remove-me: 2px)
1196
+ .nested
1197
+ @include theme($remove-me: 2px)
1198
+ @media print
1199
+ $sibling: theme()
1200
+ $after: theme(
1201
+ )
1202
+ @include theme()`);
1203
+ });
1204
+ it('should update a call another stylesheet of the app could have declared', done => {
1205
+ const themeChangesJson = {
1206
+ changes: [
1207
+ {
1208
+ name: '$remove-me', remove: true,
1209
+ owner: 'igx-theme-func',
1210
+ type: schema_1.ThemeType.Property
1211
+ }
1212
+ ]
1213
+ };
1214
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
1215
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
1216
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
1217
+ // a stylesheet of the app can forward the library's own members, and can hide or rename
1218
+ // what it declares, so what another one holds says nothing about this call
1219
+ appTree.create('_themes.scss', `@forward "igniteui-angular/theming";`);
1220
+ appTree.create('_mixins.scss', `@function igx-theme-func($remove-me) {\n @return $remove-me;\n}`);
1221
+ appTree.create('styles.scss', `@use "themes" as theme;
1222
+ @use "mixins" as custom;
1223
+ $var1: theme.igx-theme-func($remove-me: 6px);
1224
+ $var2: igx-theme-func($remove-me: 6px);`);
1225
+ const update = new UnitUpdateChanges(__dirname, appTree);
1226
+ update.applyChanges();
1227
+ expect(appTree.readContent('styles.scss')).toEqual(`@use "themes" as theme;
1228
+ @use "mixins" as custom;
1229
+ $var1: theme.igx-theme-func();
1230
+ $var2: igx-theme-func();`);
1231
+ done();
1232
+ });
1233
+ it('should not let another stylesheet of the app block a migration', done => {
1234
+ const themeChangesJson = {
1235
+ changes: [
1236
+ {
1237
+ name: '$remove-me', remove: true,
1238
+ owner: 'igx-theme-func',
1239
+ type: schema_1.ThemeType.Property
1240
+ }
1241
+ ]
1242
+ };
1243
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
1244
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
1245
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
1246
+ const unrelated = `@mixin igx-theme-func($remove-me: 1px) {\n border-width: $remove-me;\n}`;
1247
+ appTree.create('unrelated.scss', unrelated);
1248
+ appTree.create('styles.scss', `$var: igx-theme-func($remove-me: 6px);`);
1249
+ const update = new UnitUpdateChanges(__dirname, appTree);
1250
+ update.applyChanges();
1251
+ expect(appTree.readContent('unrelated.scss')).toEqual(unrelated);
1252
+ expect(appTree.readContent('styles.scss')).toEqual(`$var: igx-theme-func();`);
1253
+ done();
1254
+ });
1255
+ it('should update a theme function whose name the owner is only part of', done => {
1256
+ // migrations rely on this: `circular-theme` addresses `progress-circular-theme`,
1257
+ // and `theme` covers every `*-theme` mixin
1258
+ const themeChangesJson = {
1259
+ changes: [
1260
+ {
1261
+ name: '$progress-circle-color', replaceWith: '$fill-color-default',
1262
+ owner: 'circular-theme',
1263
+ type: schema_1.ThemeType.Property
1264
+ },
1265
+ {
1266
+ name: '$wc', remove: true,
1267
+ owner: 'theme',
1268
+ type: schema_1.ThemeType.Property
1269
+ }
1270
+ ]
1271
+ };
1272
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
1273
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
1274
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
1275
+ appTree.create('styles.scss', `$var1: progress-circular-theme($progress-circle-color: red);
1276
+ @include theme($palette: $p, $wc: true);
1277
+ $var2: light-theme($palette: $p, $wc: true);`);
1278
+ const update = new UnitUpdateChanges(__dirname, appTree);
1279
+ update.applyChanges();
1280
+ expect(appTree.readContent('styles.scss')).toEqual(`$var1: progress-circular-theme($fill-color-default: red);
1281
+ @include theme($palette: $p);
1282
+ $var2: light-theme($palette: $p);`);
1283
+ done();
1284
+ });
1285
+ it('should rename a theme property behind a comment that mentions it', done => {
1286
+ const themeChangesJson = {
1287
+ changes: [
1288
+ {
1289
+ name: '$replace-me', replaceWith: '$replaced',
1290
+ owner: 'igx-theme-func',
1291
+ type: schema_1.ThemeType.Property
1292
+ }
1293
+ ]
1294
+ };
1295
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
1296
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
1297
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
1298
+ appTree.create('styles.scss', `$var1: igx-theme-func(/* $replace-me docs */ $replace-me: 6px);
1299
+ $var2: igx-theme-func(
1300
+ $prop1: red,
1301
+ // $replace-me: keep in sync with the brand color
1302
+ $replace-me: 6px
1303
+ );`);
1304
+ const update = new UnitUpdateChanges(__dirname, appTree);
1305
+ update.applyChanges();
1306
+ expect(appTree.readContent('styles.scss')).toEqual(`$var1: igx-theme-func(/* $replace-me docs */ $replaced: 6px);
1307
+ $var2: igx-theme-func(
1308
+ $prop1: red,
1309
+ // $replace-me: keep in sync with the brand color
1310
+ $replaced: 6px
1311
+ );`);
1312
+ done();
1313
+ });
1314
+ it('should remove rather than rename a theme property the call already passes under its new name', done => {
1315
+ const themeChangesJson = {
1316
+ changes: [
1317
+ {
1318
+ name: '$replace-me', replaceWith: '$replaced',
1319
+ owner: 'igx-theme-func',
1320
+ type: schema_1.ThemeType.Property
1321
+ }
1322
+ ]
1323
+ };
1324
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
1325
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
1326
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
1327
+ appTree.create('styles.scss', `$var1: igx-theme-func($replaced: red, $replace-me: 6px);
1328
+ $var2: igx-theme-func($replace-me: 6px, $replaced: red);
1329
+ $var3: igx-theme-func($not-replaced: red, $replace-me: 6px);
1330
+ $var4: igx-theme-func($prop1: igx-inner-func($replaced: red), $replace-me: 6px);`);
1331
+ const update = new UnitUpdateChanges(__dirname, appTree);
1332
+ update.applyChanges();
1333
+ expect(appTree.readContent('styles.scss')).toEqual(`$var1: igx-theme-func($replaced: red);
1334
+ $var2: igx-theme-func( $replaced: red);
1335
+ $var3: igx-theme-func($not-replaced: red, $replaced: 6px);
1336
+ $var4: igx-theme-func($prop1: igx-inner-func($replaced: red), $replaced: 6px);`);
1337
+ done();
1338
+ });
1339
+ it('should keep the line endings of the stylesheet when carrying a comment over', done => {
1340
+ const themeChangesJson = {
1341
+ changes: [
1342
+ {
1343
+ name: '$remove-me', remove: true,
1344
+ owner: 'igx-theme-func',
1345
+ type: schema_1.ThemeType.Property
1346
+ }
1347
+ ]
1348
+ };
1349
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
1350
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
1351
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
1352
+ appTree.create('styles.scss', `$var: igx-theme-func($prop1: red, // brand color\r\n $remove-me: 6px, $prop2: blue);`);
1353
+ const update = new UnitUpdateChanges(__dirname, appTree);
1354
+ update.applyChanges();
1355
+ expect(appTree.readContent('styles.scss'))
1356
+ .toEqual(`$var: igx-theme-func($prop1: red, // brand color\r\n $prop2: blue);`);
1357
+ done();
1358
+ });
1359
+ it('should update theme properties in stylesheets outside sourceRoot', done => {
1360
+ appTree = (0, setup_spec_1.setupTestTree)({
1361
+ projects: {
1362
+ testProj: {
1363
+ projectType: 'application',
1364
+ root: '',
1365
+ sourceRoot: 'src',
1366
+ architect: { build: { options: {} } }
1367
+ }
1368
+ },
1369
+ schematics: {
1370
+ '@schematics/angular:component': {
1371
+ prefix: 'app'
1372
+ }
1373
+ }
1374
+ });
1375
+ const themeChangesJson = {
1376
+ changes: [
1377
+ {
1378
+ name: '$remove-me', remove: true,
1379
+ owner: 'igx-theme-func',
1380
+ type: schema_1.ThemeType.Property
1381
+ }
1382
+ ]
1383
+ };
1384
+ const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
1385
+ spyOn(fs, 'existsSync').and.callFake((filePath) => filePath === jsonPath);
1386
+ spyOn(fs, 'readFileSync').and.callFake(() => JSON.stringify(themeChangesJson));
1387
+ const call = `$var: igx-theme-func($remove-me: 1px);`;
1388
+ const migrated = `$var: igx-theme-func();`;
1389
+ const updated = ['src/app/app.scss', 'src/app/distribution/_summary.scss', 'styles/_theme.scss'];
1390
+ const skipped = ['node_modules/some-lib/_lib.scss', 'dist/app/styles.scss'];
1391
+ [...updated, ...skipped].forEach(file => appTree.create(file, call));
1392
+ const update = new UnitUpdateChanges(__dirname, appTree);
1393
+ update.applyChanges();
1394
+ updated.forEach(file => expect(appTree.readContent(file)).withContext(file).toEqual(migrated));
1395
+ // dependencies and build output are never touched
1396
+ skipped.forEach(file => expect(appTree.readContent(file)).withContext(file).toEqual(call));
1397
+ done();
1398
+ });
570
1399
  it('should replace imports', done => {
571
1400
  const importsJson = {
572
1401
  changes: [