Skip to content
Snippets Groups Projects
Commit 3d149422 authored by Tormod Nygård's avatar Tormod Nygård
Browse files

Merge branch 'master' into 28-add-sass-styling-to-components (#28)

parents f64a0897 ac5a4d67
Branches
No related tags found
1 merge request!26"Add Sass styling to components"
Showing
with 1925 additions and 19919 deletions
Source diff could not be displayed: it is too large. Options to address this: view the blob.
......@@ -20,16 +20,16 @@
"@angular/platform-browser-dynamic": "~11.1.1",
"@angular/router": "~11.1.1",
"@auth0/angular-jwt": "^5.0.2",
"rxjs": "~6.6.0",
"rxjs": "^6.6.6",
"tslib": "^2.0.0",
"zone.js": "~0.11.3"
"zone.js": "^0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1101.2",
"@angular/cli": "~11.1.2",
"@angular-devkit/build-angular": "^0.1101.4",
"@angular/cli": "^11.1.4",
"@angular/compiler-cli": "~11.1.1",
"@types/jasmine": "~3.6.0",
"@types/node": "^12.11.1",
"@types/jasmine": "^3.6.6",
"@types/node": "^12.20.5",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
......@@ -41,6 +41,6 @@
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.1.2"
"typescript": "^4.1.5"
}
}
......@@ -8,6 +8,8 @@ import { UserLoginFormComponent } from './users/user-login-form/user-login-form.
import { UserProfileComponent } from './users/user-profile/user-profile.component';
import { UserGuestProfileComponent } from './users/user-guest-profile/user-guest-profile.component';
import { HomeComponent } from './home/home.component';
import { UserProfileEditFormComponent } from './users/user-profile-edit-form/user-profile-edit-form.component';
const routes: Routes = [
{ path: '', component: HomeComponent },
......@@ -18,7 +20,8 @@ const routes: Routes = [
{ path: 'annonse/:id', component: PostDetailsComponent },
{ path: 'user/:id', component: UserGuestProfileComponent },
{ path: 'profile', component: UserProfileComponent },
{ path: 'profil', component: UserProfileComponent },
{ path: 'profil/rediger', component: UserProfileEditFormComponent},
{ path: 'register', component: UserRegistrationFormComponent },
{ path: 'login', component: UserLoginFormComponent }
];
......
......@@ -6,7 +6,7 @@
<span (click)="navigate('/annonse/ny')">Lag annonse</span>
<span *ngIf="!user.getUserId" (click)="navigate('/register')">Registrer</span>
<span *ngIf="!user.getUserId" (click)="navigate('/login')">Logg inn</span>
<span *ngIf="user.getUserId" (click)="navigate('/profile')">Profil</span>
<span *ngIf="user.getUserId" (click)="navigate('/profil')">Profil</span>
<span *ngIf="user.getUserId" (click)="logout()">Logg ut</span>
</nav>
</div>
......@@ -24,7 +24,9 @@
</div>
</div>
<div class="footer">
<div class="logo"><h1>SELLPOINT LOGO</h1></div>
<div class="logo">
<h1>SELLPOINT LOGO</h1>
</div>
<div class="info">
<div class="bedrift">
<h3>For bedrifter</h3>
......
......@@ -7,6 +7,7 @@ export class User implements Deserializable, Serializable {
private email: string;
private password: string;
private create_time: Date;
private isAdmin: number;
constructor(input: any = null) {
if (input) {
......@@ -17,11 +18,13 @@ export class User implements Deserializable, Serializable {
this.email = null;
this.password = null;
this.create_time = new Date();
this.isAdmin = 0;
}
}
deserialize(input: Object): this {
Object.assign(this, input);
console.log(this);
return this;
}
......@@ -31,7 +34,8 @@ export class User implements Deserializable, Serializable {
username: this.username,
email: this.email,
password: this.password,
create_time: this.create_time
create_time: this.create_time,
isAdmin: this.isAdmin,
};
}
......@@ -75,4 +79,11 @@ export class User implements Deserializable, Serializable {
this.create_time = create_time;
}
get getIsAdmin() {
return this.isAdmin;
}
set setIsAdmin(isAdmin: number) {
isAdmin = this.isAdmin;
}
}
\ No newline at end of file
......@@ -15,14 +15,20 @@ export class PostDetailsComponent implements OnInit {
post: Post = new Post();
owner: User = new User();
user: User = new User();
isAdmin: number = 0;
userId: number = 0;
constructor(private postService: PostService, private activatedRoute: ActivatedRoute, private router: Router,
private authService: AuthService, private userService: UserService) { }
ngOnInit() {
// Gets ID from current user
this.userId = this.authService.getCurrentUser(false).getUserId;
// Gets current user information
this.user = this.authService.getCurrentUser(false);
// If user is logged in, assign userId and isAdmin
this.userId = this.user.getUserId; // 0
this.isAdmin = this.user.getIsAdmin; // 0
// Gets id parameter from URL
const id = this.activatedRoute.snapshot.params["id"];
......
<div class="registrationForm">
<h3>Rediger bruker</h3>
<app-text-input [(inputModel)]="username" label="Brukernavn" (blur)="checkForm()"></app-text-input>
<app-text-input [(inputModel)]="email" label="Epost" (blur)="checkForm()"></app-text-input>
<app-password-input [(inputModel)]="password" label="Passord" (blur)="checkForm()"></app-password-input>
<app-password-input [(inputModel)]="confirm_password" label="Bekreft passord" (blur)="checkForm()">
</app-password-input>
<p>{{statusMessage}}</p>
<app-button (click)="updateUser()" text="Lagre endringer"></app-button>
</div>
\ No newline at end of file
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ComponentFixture, TestBed , fakeAsync} from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { Router } from '@angular/router';
import { UserService } from '../user.service';
import { SharedModule } from 'src/app/shared/shared.module';
import { FormsModule } from '@angular/forms';
import { UserProfileEditFormComponent } from './user-profile-edit-form.component';
import { UserProfileComponent } from '../user-profile/user-profile.component';
describe('UserProfileEditFormComponent', () => {
let component: UserProfileEditFormComponent;
let fixture: ComponentFixture<UserProfileEditFormComponent>;
let router: Router;
let mockUserService;
beforeEach(async () => {
// UserService mock setup
mockUserService = jasmine.createSpyObj(['updateUser']);
mockUserService.updateUser.and.returnValue(
new Promise<string>(
(resolve) => {
resolve("success")
})
);
});
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ UserProfileEditFormComponent ],
imports: [
HttpClientTestingModule,
FormsModule,
SharedModule,
RouterTestingModule.withRoutes([
{ path: 'profil', component: UserProfileComponent}
])
],
providers: [ { provide: UserService, useValue: mockUserService } ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(UserProfileEditFormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
router = TestBed.inject(Router);
});
it('should validate form', () => {
// Tests all if-sentences in checkForm
expect(component.checkForm()).toBeFalse();
expect(component.statusMessage).toBe("Brukernavn kan ikke være tom");
component.username = "Username";
expect(component.checkForm()).toBeFalse();
expect(component.statusMessage).toBe("Eposten kan ikke være tom");
component.email = "Email";
expect(component.checkForm()).toBeFalse();
expect(component.statusMessage).toBe("Passordet kan ikke være tom");
component.password = "password";
expect(component.checkForm()).toBeFalse();
expect(component.statusMessage).toBe("Passordet kan ikke være tom");
component.confirm_password = "hei";
expect(component.checkForm()).toBeFalse();
expect(component.statusMessage).toBe("Passordene gitt samsvarer ikke");
component.confirm_password = "password";
expect(component.checkForm()).toBeTrue();
expect(component.statusMessage).toBe("");
});
it('should not update invalid user', fakeAsync(() => {
// Tests that updating should not happen when user is invalid
component.updateUser();
expect(component.statusMessage).toBe("Brukernavn kan ikke være tom");
}));
it('should route after updating user', () => {
// Tests that url is changed after user is updated
component.username = "Username";
component.email = "Email";
component.password = "Password";
component.confirm_password = "Password";
component.updateUser();
expect(mockUserService.updateUser).toHaveBeenCalled();
expect(router.url).toBe('/');
});
});
import { Component, OnInit } from '@angular/core';
import { AuthService } from 'src/app/authentication/auth.service';
import { User } from 'src/app/models/user.model';
import { UserService } from '../user.service';
import { Router } from '@angular/router';
interface IUserLogin {
username: string;
password: string;
}
@Component({
selector: 'app-user-profile-edit-form',
templateUrl: './user-profile-edit-form.component.html',
styleUrls: ['./user-profile-edit-form.component.scss']
})
export class UserProfileEditFormComponent implements OnInit {
constructor(private userService: UserService, private authService: AuthService, private router: Router) { }
user: User = new User();
username: string = "";
email: string = "";
password: string = "";
confirm_password: string = "";
statusMessage: string = "";
ngOnInit(): void {
this.user = this.authService.getCurrentUser();
// Gets all user information and displays them in the component
if (this.user.getUserId !== 0) {
this.username = this.user.getUsername;
this.email = this.user.getEmail;
this.password = this.user.getPassword;
this.confirm_password = this.user.getPassword;
} else {
console.log("Error getting user information!");
}
}
/**
* Validates the form
*/
checkForm(): boolean {
if (this.username == "") {
this.setStatusMessage("Brukernavn kan ikke være tom");
return false;
}
else if (this.email == "") {
this.setStatusMessage("Eposten kan ikke være tom");
return false;
}
else if (this.password == "") {
this.setStatusMessage("Passordet kan ikke være tomt");
return false;
}
else if (this.confirm_password == "") {
this.setStatusMessage("Passordet kan ikke være tomt");
return false;
}
else if (this.confirm_password !== this.password) {
this.setStatusMessage("Passordene gitt samsvarer ikke");
return false;
}
this.setStatusMessage("");
return true;
}
/**
* Updates the user if given arguments are valid
*/
updateUser() {
if (this.checkForm()) {
const updatedUser = new User({
username: this.username,
email: this.email,
password: this.password,
});
const loginUser: IUserLogin = {
username: this.username,
password: this.password,
};
//
// Updates user in database and redirects to the profile page afterwards
this.userService.updateUser(updatedUser,this.user.getUserId).then(status => {
console.log("User was updated: " + JSON.stringify(status));
this.authService.login(loginUser).then(() =>
this.router.navigateByUrl("/profil")
);
}).catch(error => {
console.log("Error updating user: " + error);
});
}
}
/**
* Sets the status message for user feedback on form submit
*/
setStatusMessage(message: string) {
this.statusMessage = message;
}
}
......@@ -3,14 +3,16 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AuthService } from 'src/app/authentication/auth.service';
import { User } from 'src/app/models/user.model';
import { SharedModule } from 'src/app/shared/shared.module';
import { UserLoginFormComponent } from '../user-login-form/user-login-form.component';
import { UserService } from '../user.service';
import { UserProfileComponent } from './user-profile.component';
describe('UserProfileComponent', () => {
let component: UserProfileComponent;
let fixture: ComponentFixture<UserProfileComponent>;
let mockAuthService;
let mockAuthService, mockUserService;
beforeEach(async () => {
// AuthService mock setup
......@@ -20,9 +22,19 @@ describe('UserProfileComponent', () => {
username: "tester",
email: "test@test.com",
password: "1234",
create_time: 513498
create_time: 513498,
isAdmin: 0
}));
// UserService mock setup
mockUserService = jasmine.createSpyObj(['deleteUser']);
mockUserService.deleteUser.and.returnValue(
new Promise<any>(
(resolve) => {
resolve({data: []})
})
);
await TestBed.configureTestingModule({
declarations: [ UserProfileComponent ],
......@@ -33,7 +45,8 @@ describe('UserProfileComponent', () => {
])
],
providers: [
{ provide: AuthService, useValue: mockAuthService }
{ provide: AuthService, useValue: mockAuthService },
{ provide: UserService, useValue: mockUserService }
]
})
.compileComponents();
......@@ -57,7 +70,15 @@ describe('UserProfileComponent', () => {
username: "tester",
email: "test@test.com",
password: "1234",
create_time: 513498
create_time: 513498,
isAdmin: 0
}));
});
it('should delete current user', async () => {
// Waits for ngOnInit and checks that we can delete the current user
await fixture.whenStable();
component.deleteUser();
expect(mockUserService.deleteUser).toHaveBeenCalledWith(4);
});
});
......@@ -19,7 +19,7 @@ export class UserProfileComponent implements OnInit {
allPosts: Array<Post> = [];
user: User = new User();
constructor(private authService: AuthService, private postService: PostService, private router: Router) { }
constructor(private authService: AuthService, private userService: UserService, private postService: PostService, private router: Router) { }
ngOnInit(): void {
this.user = this.authService.getCurrentUser();
......@@ -36,4 +36,18 @@ export class UserProfileComponent implements OnInit {
console.log(error);
});
}
/**
* Deletes user in database and navigates to login
*/
deleteUser() {
this.userService.deleteUser(this.user.getUserId).then(data => {
console.log("Successfully deleted user: " + this.user.getUserId);
this.authService.logout();
this.router.navigateByUrl("/login");
}).catch(error => {
console.log(error);
});
}
}
......@@ -26,4 +26,8 @@ describe('UserRegistrationFormComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
});
......@@ -75,6 +75,7 @@ export class UserRegistrationFormComponent implements OnInit {
username: this.username,
email: this.email,
password: this.password,
isAdmin: 0,
});
// Adds user to database and redirects to the homepage afterwards
......
......@@ -7,6 +7,8 @@ import { UserRegistrationFormComponent } from './user-registration-form/user-reg
import { UserProfileComponent } from './user-profile/user-profile.component';
import { UserLoginFormComponent } from './user-login-form/user-login-form.component';
import { UserGuestProfileComponent } from './user-guest-profile/user-guest-profile.component';
import { UserProfileEditFormComponent } from './user-profile-edit-form/user-profile-edit-form.component';
@NgModule({
......@@ -14,7 +16,8 @@ import { UserGuestProfileComponent } from './user-guest-profile/user-guest-profi
UserRegistrationFormComponent,
UserProfileComponent,
UserLoginFormComponent,
UserGuestProfileComponent
UserGuestProfileComponent,
UserProfileEditFormComponent
],
imports: [
CommonModule,
......
......@@ -40,6 +40,7 @@ describe('UserService', () => {
email: "blob@planet.us",
password: "Hyttepine",
create_time: 1613552549000,
isAdmin: 0
}]
});
});
......@@ -59,6 +60,7 @@ describe('UserService', () => {
username: "zorg",
email: "blob@planet.us",
password: "Hyttepine",
isAdmin: 0
}]
});
});
......@@ -76,5 +78,34 @@ describe('UserService', () => {
});
});
describe('deleteUser', () => {
it('should delete user', () => {
// Deletes user with id = 2
service.deleteUser(2)
.then(data => {})
.catch(error => {
fail();
});
// Mocks and checks HTTP request
const req = httpMock.expectOne("api/user/2");
expect(req.request.method).toBe("DELETE");
req.flush({
data: []
});
});
it('should reject on http error', () => {
// Deletes user with id = 2, but should catch HTTP error
service.deleteUser(2).then(data => {
fail();
}).catch(error => {});
// Mocks and checks HTTP request
const req = httpMock.expectOne("api/user/2");
expect(req.request.method).toBe("DELETE");
req.error(new ErrorEvent("400"));
});
});
});
......@@ -76,4 +76,50 @@ export class UserService {
private get_all_users() {
return this.http.get(this.userUrl);
}
/**
* Deletes an user from the database by id.
*/
deleteUser(id: number): Promise<User> {
return new Promise<User>(
(resolve, reject) => {
this.delete_user(id).subscribe((data: any) => {
try {
resolve(data);
} catch (err: any) {
reject(err);
}
},
(err: any) => {
console.log(err.message);
reject(err);
});
}
);
}
private delete_user(id: number) {
return this.http.delete(this.userUrl + id);
}
// /api/user/:userId
updateUser(user: User, userId: number): Promise<string> {
return new Promise<string>(
(resolve, reject) => {
this.update_user(user, userId).subscribe((data: any) => {
try {
resolve(data.data);
} catch (err: any) {
reject(err);
}
},
(err: any) => {
console.log(err.message);
reject(err);
});
}
);
}
private update_user(user: User, userId: number) {
return this.http.put(this.userUrl + userId, user.serialize());
}
}
......@@ -10,11 +10,11 @@
"author": "",
"license": "ISC",
"dependencies": {
"@types/cors": "^2.8.9",
"@types/cors": "^2.8.10",
"@types/express": "^4.17.11",
"@types/express-jwt": "^6.0.1",
"@types/jest": "^26.0.20",
"@types/mysql": "^2.15.17",
"@types/mysql": "^2.15.18",
"@types/supertest": "^2.0.10",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
......@@ -26,12 +26,12 @@
"mysql": "^2.18.1",
"mysql2": "^2.2.5",
"supertest": "^6.1.3",
"ts-jest": "^26.5.1"
"ts-jest": "^26.5.3"
},
"devDependencies": {
"@types/jsonwebtoken": "^8.5.0",
"nodemon": "^2.0.7",
"ts-node": "^9.1.1",
"typescript": "^4.1.3"
"typescript": "^4.2.3"
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment