Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1864d49d6c | |||
| 37d7df2794 | |||
| 15788dbaa6 | |||
| 3aec2d5290 | |||
| ddf92e0548 | |||
| 196ad13eea | |||
| 1eca714680 | |||
| f33bf8cd01 | |||
| 85fe20d0f3 | |||
| 8de5d29a4d | |||
| 17d2289974 | |||
| a0e56f6a89 | |||
| 8885e288ed | |||
| a888636487 | |||
| dad46d788d | |||
| 3619e33f44 | |||
| cf8e3b880c | |||
| a6e48d6b2b | |||
| 06a64fa018 | |||
| 1bec713692 | |||
| cea6d218f8 | |||
| 3dbecd171f | |||
| bdba683f00 | |||
| 15ad26c505 | |||
| e529f101b7 | |||
| 2bed3b6438 | |||
| 7d63845951 |
Vendored
+161
@@ -0,0 +1,161 @@
|
||||
pipeline {
|
||||
|
||||
agent none
|
||||
|
||||
options {
|
||||
buildDiscarder(logRotator(numToKeepStr: '10'))
|
||||
disableConcurrentBuilds()
|
||||
}
|
||||
|
||||
stages {
|
||||
|
||||
stage('Build & Test') {
|
||||
|
||||
agent { label getAgentLabel() }
|
||||
|
||||
environment {
|
||||
CHROME_BIN = "${getChromeBin()}"
|
||||
}
|
||||
|
||||
stages {
|
||||
|
||||
stage('Cleanup Workspace') {
|
||||
steps { cleanWs() }
|
||||
}
|
||||
|
||||
stage('Checkout') {
|
||||
steps { checkout scm }
|
||||
}
|
||||
|
||||
stage('Inject Environment File') {
|
||||
when {
|
||||
not {
|
||||
anyOf {
|
||||
branch pattern: "feature/.*", comparator: "REGEXP"
|
||||
branch pattern: "bug/.*", comparator: "REGEXP"
|
||||
}
|
||||
}
|
||||
}
|
||||
steps {
|
||||
configFileProvider([
|
||||
configFile(
|
||||
fileId: getEnvFileId(),
|
||||
targetLocation: 'src/environments/environment.ts',
|
||||
replaceTokens: true
|
||||
)
|
||||
]) {
|
||||
echo "Injected environment for ${env.BRANCH_NAME}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Install Dependencies') {
|
||||
steps { sh 'npm ci' }
|
||||
}
|
||||
|
||||
stage('Run Tests') {
|
||||
when {
|
||||
anyOf {
|
||||
branch pattern: "feature/.*", comparator: "REGEXP"
|
||||
branch pattern: "bug/.*", comparator: "REGEXP"
|
||||
branch 'develop'
|
||||
branch 'prod'
|
||||
changeRequest()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'npm run test -- --watch=false --browsers=ChromeHeadless'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build Angular App') {
|
||||
when {
|
||||
anyOf {
|
||||
branch 'develop'
|
||||
branch 'prod'
|
||||
changeRequest()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'ng build --configuration production --base-href /admin/'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Production Approval') {
|
||||
when { branch 'prod' }
|
||||
steps {
|
||||
input message: 'Approve deployment to PRODUCTION?', ok: 'Deploy'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Deploy & Verify') {
|
||||
|
||||
when {
|
||||
anyOf {
|
||||
branch 'develop'
|
||||
branch 'prod'
|
||||
}
|
||||
}
|
||||
|
||||
agent { label getAgentLabel() }
|
||||
|
||||
stages {
|
||||
|
||||
stage('Deploy') {
|
||||
steps {
|
||||
sh """
|
||||
rsync -av --delete dist/portfolio-admin/browser/ ${getDeployPath()}/
|
||||
sudo /usr/bin/systemctl reload nginx
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
stage('Health Check') {
|
||||
steps {
|
||||
sh """
|
||||
sleep 3
|
||||
curl -f ${getHealthUrl()}
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
success { echo "Deployment successful for ${env.BRANCH_NAME}" }
|
||||
failure { echo "Deployment failed for ${env.BRANCH_NAME}" }
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// -------- Helper Methods --------
|
||||
//
|
||||
|
||||
def getAgentLabel() {
|
||||
return env.BRANCH_NAME == 'prod' ? 'oracle-prod' : 'built-in'
|
||||
}
|
||||
|
||||
def getEnvFileId() {
|
||||
return env.BRANCH_NAME == 'prod' ? 'admin-prod-properties' : 'admin-uat-properties'
|
||||
}
|
||||
|
||||
def getDeployPath() {
|
||||
return env.BRANCH_NAME == 'prod'
|
||||
? "/var/www/bangararaju.kottedi.in/admin"
|
||||
: "/var/www/bangararaju.kottedi.in/admin"
|
||||
}
|
||||
|
||||
def getHealthUrl() {
|
||||
return env.BRANCH_NAME == 'prod'
|
||||
? "https://bangararaju.kottedi.in/admin"
|
||||
: "https://bangararaju-uat.kottedi.in/admin"
|
||||
}
|
||||
|
||||
def getChromeBin() {
|
||||
return env.BRANCH_NAME == 'prod'
|
||||
? "/snap/bin/chromium"
|
||||
: "/usr/bin/chromium"
|
||||
}
|
||||
@@ -79,6 +79,8 @@
|
||||
"test": {
|
||||
"builder": "@angular/build:karma",
|
||||
"options": {
|
||||
"main": "./src/test.ts",
|
||||
"polyfills": [],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
|
||||
Generated
+20
@@ -28,6 +28,7 @@
|
||||
"@angular/build": "^20.3.2",
|
||||
"@angular/cli": "^20.3.2",
|
||||
"@angular/compiler-cli": "^20.3.0",
|
||||
"@angular/platform-browser-dynamic": "^20.3.4",
|
||||
"@types/express": "^5.0.1",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"@types/node": "^20.17.19",
|
||||
@@ -754,6 +755,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/platform-browser-dynamic": {
|
||||
"version": "20.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-20.3.4.tgz",
|
||||
"integrity": "sha512-eeScVJyZLDTNrnEDDBgF/WZpZrjEszFFkuEzNQ43sbPjc5M7Noue38Nd9QZ664ZQ3a4ZpUpritfHvc55a/fl9Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "20.3.4",
|
||||
"@angular/compiler": "20.3.4",
|
||||
"@angular/core": "20.3.4",
|
||||
"@angular/platform-browser": "20.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/platform-server": {
|
||||
"version": "20.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-20.3.4.tgz",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"@angular/build": "^20.3.2",
|
||||
"@angular/cli": "^20.3.2",
|
||||
"@angular/compiler-cli": "^20.3.0",
|
||||
"@angular/platform-browser-dynamic": "^20.3.4",
|
||||
"@types/express": "^5.0.1",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"@types/node": "^20.17.19",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
|
||||
import { About } from './about';
|
||||
|
||||
@@ -8,7 +9,8 @@ describe('About', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [About]
|
||||
imports: [About],
|
||||
providers: [provideHttpClientTesting()]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
|
||||
import { Contact } from './contact';
|
||||
|
||||
@@ -8,7 +9,8 @@ describe('Contact', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [Contact]
|
||||
imports: [Contact],
|
||||
providers: [provideHttpClientTesting()]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (project.categories?.length) {
|
||||
@if (project.categories.length) {
|
||||
<div class="detail-section">
|
||||
<h4>Categories</h4>
|
||||
<div class="tag-list">
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
background: var(--eerie-black-2, #1e1e1e);
|
||||
border: 1px solid var(--jet, #383838);
|
||||
border: 1.2px solid hsla(45, 100%, 72%, 0.45);
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
|
||||
import { Projects } from './projects';
|
||||
|
||||
@@ -8,7 +9,8 @@ describe('Projects', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [Projects]
|
||||
imports: [Projects],
|
||||
providers: [provideHttpClientTesting()]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
|
||||
import { Resume } from './resume';
|
||||
|
||||
@@ -8,7 +9,8 @@ describe('Resume', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [Resume]
|
||||
imports: [Resume],
|
||||
providers: [provideHttpClientTesting()]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
|
||||
import { AdminService } from './admin.service';
|
||||
|
||||
@@ -6,7 +7,9 @@ describe('AdminService', () => {
|
||||
let service: AdminService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
TestBed.configureTestingModule({
|
||||
providers: [provideHttpClientTesting()]
|
||||
});
|
||||
service = TestBed.inject(AdminService);
|
||||
});
|
||||
|
||||
|
||||
+1
-7
@@ -15,11 +15,5 @@ describe('App', () => {
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, portfolio-admin');
|
||||
});
|
||||
// title rendering test removed — template doesn't render a static H1
|
||||
});
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
describe('AuthService', () => {
|
||||
let service: AuthService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
providers: [provideRouter([]), provideHttpClientTesting(), AuthService]
|
||||
}).compileComponents();
|
||||
|
||||
service = TestBed.inject(AuthService);
|
||||
});
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ export class AuthService {
|
||||
this.http.post<void>(this.api('/api/v1/auth/logout'), {}).subscribe();
|
||||
this.accessToken = null;
|
||||
this.safeRemoveToken();
|
||||
this.router.navigate(['/admin/login']);
|
||||
this.router.navigateByUrl('login');
|
||||
}
|
||||
|
||||
refreshToken(): Observable<RefreshTokenResponse> {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { provideRouter } from '@angular/router';
|
||||
|
||||
import { AuthService} from './auth.service';
|
||||
|
||||
@@ -6,7 +8,9 @@ describe('AuthService', () => {
|
||||
let service: AuthService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
TestBed.configureTestingModule({
|
||||
providers: [provideRouter([]), provideHttpClientTesting()]
|
||||
});
|
||||
service = TestBed.inject(AuthService);
|
||||
});
|
||||
|
||||
|
||||
@@ -21,19 +21,9 @@
|
||||
<!-- Step 2: OTP Input -->
|
||||
@if (isOtpSent() && !isVerified()) {
|
||||
<div class="otp-section">
|
||||
<p class="info-text">
|
||||
OTP sent to <b>{{ emailForm.value.email }}</b>
|
||||
</p>
|
||||
|
||||
<form [formGroup]="otpForm" (ngSubmit)="verifyOtp()">
|
||||
<label for="otp">Enter 6-digit OTP</label>
|
||||
<input id="otp" type="text" maxlength="6" formControlName="otp" placeholder="123456" />
|
||||
@if (isError()) {
|
||||
<div class="error-banner">
|
||||
<i class="fa-solid fa-circle-exclamation"></i>
|
||||
<span>{{ message() }}</span>
|
||||
</div>
|
||||
}
|
||||
<button type="submit" [disabled]="otpForm.invalid">
|
||||
Verify OTP
|
||||
</button>
|
||||
@@ -47,5 +37,19 @@
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (isError()) {
|
||||
<div class="error-banner">
|
||||
<i class="fa-solid fa-circle-exclamation"></i>
|
||||
<span>{{ message() }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!isError() && isOtpSent() && !isVerified()) {
|
||||
<div class="success-banner">
|
||||
<i class="fa-solid fa-circle-check"></i>
|
||||
<span>{{ message() }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@@ -171,3 +171,23 @@ button {
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Success banner for OTP sent */
|
||||
.success-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: rgba(120, 255, 140, 0.07);
|
||||
border: 1px solid rgba(120, 255, 140, 0.25);
|
||||
border-radius: 8px;
|
||||
padding: 10px 14px;
|
||||
margin: 10px 0 10px;
|
||||
color: #78ff8c;
|
||||
font-size: 13px;
|
||||
|
||||
i {
|
||||
font-size: 15px;
|
||||
flex-shrink: 0;
|
||||
color: #78ff8c;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { provideRouter } from '@angular/router';
|
||||
|
||||
import { OtpComponent } from './otp.component';
|
||||
|
||||
@@ -8,7 +10,8 @@ describe('OtpComponent', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [OtpComponent]
|
||||
imports: [OtpComponent],
|
||||
providers: [provideRouter([]), provideHttpClientTesting()]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
||||
@@ -48,15 +48,27 @@ export class OtpComponent implements OnInit {
|
||||
if (this.emailForm.invalid) return;
|
||||
const email = this.emailForm.value.email;
|
||||
|
||||
this.authService.sendOtp(email).subscribe(() => {
|
||||
this.authService.sendOtp(email).subscribe({
|
||||
next: () => {
|
||||
this.isOtpSent.set(true);
|
||||
this.message.set('OTP sent successfully!');
|
||||
this.isError.set(false);
|
||||
this.message.set(`OTP sent to ${email}.`);
|
||||
this.startTimer(30); // 30 seconds countdown
|
||||
},
|
||||
error: (err) => {
|
||||
this.isError.set(true);
|
||||
if (err.status === 404) {
|
||||
this.message.set('Email not found. Please check and try again.');
|
||||
} else {
|
||||
this.message.set('Failed to send OTP. Please try again.');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
resendOtp() {
|
||||
if (this.countdown() > 0) return;
|
||||
this.otpForm.reset();
|
||||
this.sendOtp();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,29 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { of } from 'rxjs';
|
||||
import { HttpInterceptorFn } from '@angular/common/http';
|
||||
|
||||
import { AuthInterceptor } from './auth-interceptor';
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
|
||||
describe('AuthInterceptor', () => {
|
||||
const mockAuthService: Partial<AuthService> = {
|
||||
getApiKey: () => '',
|
||||
currentToken: null as unknown as string | null,
|
||||
refreshToken: () => of({ accessToken: 'new-token' }),
|
||||
safeSetToken: () => { /* no-op for testing */ },
|
||||
logout: () => { /* no-op for testing */ }
|
||||
};
|
||||
|
||||
const interceptor: HttpInterceptorFn = (req, next) =>
|
||||
TestBed.runInInjectionContext(() => AuthInterceptor(req, next));
|
||||
|
||||
beforeEach(() => TestBed.configureTestingModule({
|
||||
providers: [
|
||||
AuthInterceptor
|
||||
{ provide: AuthService, useValue: mockAuthService }
|
||||
]
|
||||
}));
|
||||
|
||||
it('should be created', () => {
|
||||
const interceptor: AuthInterceptor = TestBed.inject(AuthInterceptor);
|
||||
expect(interceptor).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { provideRouter } from '@angular/router';
|
||||
|
||||
import { AdminLayout } from './admin-layout';
|
||||
|
||||
@@ -8,7 +10,8 @@ describe('AdminLayout', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [AdminLayout]
|
||||
imports: [AdminLayout],
|
||||
providers: [provideRouter([]), provideHttpClientTesting()]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
/* Dynamic form spacing and material tweaks to match popup theme */
|
||||
.full-width { width: 100%; display: block; }
|
||||
.full-width {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
mat-form-field.full-width { margin-bottom: 12px; }
|
||||
mat-form-field.full-width {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
/* Reduce label size slightly to fit popup */
|
||||
mat-form-field .mat-form-field-label { font-size: 0.95rem; color: var(--light-gray-70); }
|
||||
mat-form-field .mat-form-field-label {
|
||||
font-size: 0.95rem;
|
||||
color: var(--light-gray-70);
|
||||
}
|
||||
|
||||
/* Make input text contrast better */
|
||||
.mat-input-element,
|
||||
@@ -15,10 +23,16 @@ textarea.mat-mdc-input-element,
|
||||
}
|
||||
|
||||
/* Smaller helper/hint text */
|
||||
.mat-hint { color: var(--light-gray-70); font-size: 0.85rem; }
|
||||
.mat-hint {
|
||||
color: var(--light-gray-70);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* Error styling consistent with theme */
|
||||
mat-error { color: #ff8a80; font-size: 0.9rem; }
|
||||
mat-error {
|
||||
color: #ff8a80;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Outline border always visible, highlight on focus */
|
||||
::ng-deep .mdc-notched-outline__leading,
|
||||
@@ -34,8 +48,18 @@ mat-error { color: #ff8a80; font-size: 0.9rem; }
|
||||
}
|
||||
|
||||
/* Make array item fields inline on larger screens */
|
||||
.array-item-fields { display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap; }
|
||||
.array-item-fields mat-form-field { flex: 1 1 auto; margin-bottom: 0; min-width: 120px; }
|
||||
.array-item-fields {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: baseline;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.array-item-fields mat-form-field {
|
||||
flex: 1 1 auto;
|
||||
margin-bottom: 0;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
/* Stack array item fields vertically on small screens */
|
||||
@media (max-width: 580px) {
|
||||
@@ -83,6 +107,7 @@ mat-error { color: #ff8a80; font-size: 0.9rem; }
|
||||
|
||||
/* Force material label/input colors to match dark popup theme (covers MDC + legacy classes) */
|
||||
.themed-popup {
|
||||
|
||||
::ng-deep .mat-form-field-label,
|
||||
::ng-deep .mat-mdc-floating-label,
|
||||
::ng-deep .mat-form-field .mat-form-field-label {
|
||||
@@ -107,6 +132,7 @@ mat-error { color: #ff8a80; font-size: 0.9rem; }
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.form-array {
|
||||
margin-bottom: 20px;
|
||||
padding: 10px 0;
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DynamicForm } from './dynamic-form';
|
||||
import { DynamicFormComponent } from './dynamic-form';
|
||||
import { DynamicFormConfig } from './dynamic-form-config';
|
||||
|
||||
describe('DynamicForm', () => {
|
||||
let component: DynamicForm;
|
||||
let fixture: ComponentFixture<DynamicForm>;
|
||||
let component: DynamicFormComponent;
|
||||
let fixture: ComponentFixture<DynamicFormComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [DynamicForm]
|
||||
imports: [DynamicFormComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DynamicForm);
|
||||
fixture = TestBed.createComponent(DynamicFormComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.config = { fields: [] } as unknown as DynamicFormConfig;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
|
||||
import { DynamicPopupComponent } from './dynamic-popup';
|
||||
import { DynamicFormConfig } from '../dynamic-form/dynamic-form-config';
|
||||
|
||||
describe('DynamicPopupComponent', () => {
|
||||
let component: DynamicPopupComponent;
|
||||
@@ -8,12 +10,15 @@ describe('DynamicPopupComponent', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [DynamicPopupComponent]
|
||||
imports: [DynamicPopupComponent],
|
||||
providers: [provideHttpClientTesting()]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DynamicPopupComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.config = { title: 'Test', submitLabel: 'Save', fields: [] } as DynamicFormConfig;
|
||||
component.data = {};
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
|
||||
+4
-5
@@ -507,9 +507,9 @@
|
||||
.mat-mdc-text-field.mat-mdc-focused .mat-mdc-notched-outline .mat-mdc-notched-outline-path {
|
||||
stroke: var(--orange-yellow-crayola) !important;
|
||||
border-color: var(--orange-yellow-crayola) !important;
|
||||
|
||||
}
|
||||
/* Additional overrides: set MDC theme variables and force a gold focus ring */
|
||||
.themed-popup {
|
||||
// .themed-popup {
|
||||
/* Override MDC primary color used by many Material components */
|
||||
--mdc-theme-primary: var(--orange-yellow-crayola);
|
||||
--mdc-theme-on-primary: var(--smoky-black);
|
||||
@@ -529,9 +529,8 @@
|
||||
.mat-mdc-text-field.mat-mdc-focused .mat-mdc-notched-outline-path {
|
||||
stroke: var(--orange-yellow-crayola) !important;
|
||||
}
|
||||
}
|
||||
box-shadow: 0 6px 18px rgba(36,26,18,0.12) !important;
|
||||
}
|
||||
// }
|
||||
// box-shadow: 0 6px 18px rgba(36,26,18,0.12) !important;
|
||||
}
|
||||
|
||||
.contacts-list {
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// Test bootstrap for Karma
|
||||
import { provideZonelessChangeDetection } from '@angular/core';
|
||||
import { getTestBed, TestBed } from '@angular/core/testing';
|
||||
import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
|
||||
declare const beforeEach: (fn: () => void) => void;
|
||||
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserTestingModule,
|
||||
platformBrowserTesting()
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [provideZonelessChangeDetection(), provideHttpClient(), provideHttpClientTesting()]
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user