Merge branch 'develop'

This commit is contained in:
2019-08-08 22:16:45 +02:00
15 changed files with 476 additions and 45 deletions

Binary file not shown.

View File

@@ -0,0 +1,58 @@
package org.codiki.core.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.ServletWebRequest;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* Controller that catch errors from spring rest or spring security and others, and transform them to JSON response.
*/
@RestController
@RequestMapping("/error")
public class CustomErrorController implements ErrorController {
private final ErrorAttributes errorAttributes;
@Autowired
public CustomErrorController(ErrorAttributes errorAttributes) {
Assert.notNull(errorAttributes, "ErrorAttributes must not be null");
this.errorAttributes = errorAttributes;
}
@Override
public String getErrorPath() {
return "/error";
}
@RequestMapping
public Map<String, Object> error(HttpServletRequest request){
Map<String, Object> body = getErrorAttributes(request, getTraceParameter(request));
String trace = (String) body.get("trace");
if(trace != null){
String[] lines = trace.split("\n\t");
body.put("trace", lines);
}
return body;
}
private boolean getTraceParameter(HttpServletRequest request) {
String parameter = request.getParameter("trace");
if (parameter == null) {
return false;
}
return !"false".equals(parameter.toLowerCase());
}
private Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
return errorAttributes.getErrorAttributes(new ServletWebRequest(request), includeStackTrace);
}
}

View File

@@ -0,0 +1,24 @@
package org.codiki.core.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@RestController
public class RobotsTxtController {
private static final Logger LOG = LoggerFactory.getLogger(RobotsTxtController.class);
@RequestMapping(value = "/robots.txt")
public void robots(HttpServletResponse response) {
try {
response.getWriter().write("User-agent: *\nDisallow: /\n");
} catch (IOException ex) {
LOG.info("Error during robots.txt serving.", ex);
}
}
}

View File

@@ -23,6 +23,6 @@ public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override @Override
public void commence(HttpServletRequest request, HttpServletResponse response, public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException { AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
} }
} }

View File

@@ -45,27 +45,31 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() http.authorizeRequests()
// Permits all
.antMatchers( .antMatchers(
"/api/account/login", "/api/account/login",
"/api/account/logout", "/api/account/logout",
"/api/account/signin" "/api/account/signin",
"/robots.txt"
).permitAll()
.antMatchers(
HttpMethod.GET,
"/api/categories",
"/api/images",
"/api/posts",
"/api/categories/**",
"/api/images/**",
"/api/posts/**"
).permitAll() ).permitAll()
.antMatchers( .antMatchers(
"/api/images/uploadAvatar", "/api/images/uploadAvatar",
"/api/images/myImages", "/api/images/myImages",
"/api/posts/myPosts",
"/api/account/changePassword", "/api/account/changePassword",
"/api/account/" "/api/account/",
"/api/posts/myPosts",
"/api/posts/preview",
"/api/posts/"
).authenticated() ).authenticated()
.antMatchers(
HttpMethod.GET,
"/api/categories",
"/api/images",
"/api/posts",
"/api/categories/**",
"/api/images/**",
"/api/posts/**"
).permitAll()
.anyRequest().permitAll() .anyRequest().permitAll()
.and() .and()
// Allow to avoid login form at authentication failure from Angular app // Allow to avoid login form at authentication failure from Angular app

View File

@@ -21,6 +21,9 @@ logging:
server: server:
# use-forward-headers=true # use-forward-headers=true
error:
whitelabel:
enabled: false # Disable html error responses.
port: 8080 port: 8080
# ssl: # ssl:
# key-store: /home/takiguchi/Developpement/Java/codiki/keystore.p12 # key-store: /home/takiguchi/Developpement/Java/codiki/keystore.p12

View File

@@ -39,9 +39,9 @@
<a routerLink="/accountSettings" class="indigo-text"> <a routerLink="/accountSettings" class="indigo-text">
Annuler Annuler
</a> </a>
<button class="float-right waves-effect waves-light indigo btn" <button class="float-right waves-effect waves-light indigo white-text btn"
type="submit" [disabled]="!profilEditionForm.form.valid"> type="submit" [disabled]="!profilEditionForm.form.valid">
Suivant Enregistrer
</button> </button>
</div> </div>
</form> </form>

View File

@@ -80,7 +80,7 @@ export class ProfilEditionComponent implements OnInit {
onSubmit(): void { onSubmit(): void {
this.profilEditionService.updateUser(this.model).subscribe(() => { this.profilEditionService.updateUser(this.model).subscribe(() => {
NotificationsComponent.success('Modification enregistrée.'); NotificationsComponent.success('Modifications enregistrées.');
}, error => { }, error => {
NotificationsComponent.error('L\'adresse mail saisie n\'est pas disponible.'); NotificationsComponent.error('L\'adresse mail saisie n\'est pas disponible.');
}); });

View File

@@ -4,6 +4,7 @@ import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/c
import { Observable, throwError } from 'rxjs'; import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators'; import { catchError } from 'rxjs/operators';
import { AuthService } from '../services/auth.service'; import { AuthService } from '../services/auth.service';
import { NotificationsComponent } from '../notifications/notifications.component';
@Injectable() @Injectable()
export class UnauthorizedInterceptor implements HttpInterceptor { export class UnauthorizedInterceptor implements HttpInterceptor {
@@ -17,6 +18,7 @@ export class UnauthorizedInterceptor implements HttpInterceptor {
if (err.status === 401) { if (err.status === 401) {
this.authService.setAnonymous(); this.authService.setAnonymous();
this.router.navigate(['/login']); this.router.navigate(['/login']);
window.setTimeout(() => NotificationsComponent.error('Veuillez vous authentifier pour réaliser cette action.'), 500);
} }
const error = (err.error && err.error.message) || err.statusText; const error = (err.error && err.error.message) || err.statusText;

View File

@@ -0,0 +1,27 @@
<div class="post-card hoverable" routerLink="/posts/{{post.key}}">
<mdb-card>
<!-- Picture -->
<mdb-card-img [src]="post.image" alt="Article"></mdb-card-img>
<!-- Body -->
<mdb-card-body>
<!-- Title -->
<mdb-card-title>
<h4>{{post.title}}</h4>
</mdb-card-title>
<!-- Description -->
<mdb-card-text>
{{post.description}}
</mdb-card-text>
</mdb-card-body>
<!-- Footer -->
<div class="card-footer text-muted mt-4">
<img [src]="getAvatarUrl()"
class="author-img"
[alt]="post.author.name"
[mdbTooltip]="post.author.name"
placement="bottom"/>
Article écrit par {{post.author.name}}
<span class="creation-date-area">({{post.creationDate | date:'yyyy-MM-dd HH:mm:ss'}})</span>
</div>
</mdb-card>
</div>

View File

@@ -3,35 +3,11 @@ import { Post } from '../entities';
@Component({ @Component({
selector: 'app-post-card', selector: 'app-post-card',
template: ` templateUrl: './post-card.component.html',
<div class="card hoverable">
<div class="view hm-white-slight waves-light" mdbRippleRadius>
<img id="post-image" class="img-fluid" [src]="post.image" alt="Article" />
<a routerLink="/posts/{{post.key}}">
<div class="mask"></div>
</a>
</div>
<div class="card-body">
<h4 class="card-title">{{post.title}}</h4>
<p class="card-text">{{post.description}}</p>
</div>
<div class="card-data">
<img [src]="getAvatarUrl()"
class="author-img"
[alt]="post.author.name"
[mdbTooltip]="post.author.name"
placement="bottom"/>
Article écrit par {{post.author.name}}
<span class="creation-date-area">({{post.creationDate | date:'yyyy-MM-dd HH:mm:ss'}})</span>
</div>
</div>`,
styles: [` styles: [`
div.card { .post-card {
margin-bottom: 50px; margin-bottom: 50px;
} cursor: pointer;
.card .card-data {
padding: 15px;
background-color: #f5f5f5;
} }
.creation-date-area { .creation-date-area {
color: #bdbdbd; color: #bdbdbd;

View File

@@ -159,11 +159,17 @@ export class CreateUpdatePostComponent implements OnInit {
if (this.model.key) { if (this.model.key) {
this.createUpdatePostService.updatePost(this.model).subscribe(post => { this.createUpdatePostService.updatePost(this.model).subscribe(post => {
NotificationsComponent.error('Modification enregistrée'); NotificationsComponent.success('Modification enregistrée');
}, error => {
console.log(error);
NotificationsComponent.error('Une erreur est survenue lors de l\'enregistrement des modifications.');
}); });
} else { } else {
this.createUpdatePostService.addPost(this.model).subscribe(post => { this.createUpdatePostService.addPost(this.model).subscribe(post => {
this.router.navigate([`/posts/update/${post.key}`]); this.router.navigate([`/posts/update/${post.key}`]);
}, error => {
console.log(error);
NotificationsComponent.error('Une erreur est survenue lors de la création du post.');
}); });
} }
} else { } else {

Binary file not shown.

View File

@@ -3,7 +3,8 @@
<head> <head>
<title>Codiki</title> <title>Codiki</title>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="robots" content="noindex, nofollow" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<base href="/" /> <base href="/" />
<link rel="icon" type="image/x-icon" href="assets/images/favicon.png" /> <link rel="icon" type="image/x-icon" href="assets/images/favicon.png" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" /> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />

View File

@@ -0,0 +1,330 @@
package org.codiki.core.services;
import org.apache.commons.lang.StringEscapeUtils;
import org.junit.Before;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class ParserServiceTest {
private ParserService parserService;
@Before
public void setUp() {
parserService = new ParserService();
}
/* *********************************************************** */
/* H E A D E R S P A R S I N G */
/* *********************************************************** */
@Test
public void testParseHeaders_h1() {
final String strInit = "[h1]Header[/h1]";
final String strExpected = "<h1>Header</h1>";
assertThat(parserService.parseHeaders(strInit)).isEqualTo(strExpected);
}
@Test
public void testParseHeaders_h2() {
final String strInit = "[h2]Header[/h2]";
final String strExpected = "<h2>Header</h2>";
assertThat(parserService.parseHeaders(strInit)).isEqualTo(strExpected);
}
@Test
public void testParseHeaders_h3() {
final String strInit = "[h3]Header[/h3]";
final String strExpected = "<h3>Header</h3>";
assertThat(parserService.parseHeaders(strInit)).isEqualTo(strExpected);
}
@Test
public void testParseHeader_onlyCoupleOfTags() {
final String strInit = "[h1][h1]Test[/h1]";
final String strExpected = "[h1]<h1>Test</h1>";
assertThat(parserService.parseHeaders(strInit)).isEqualTo(strExpected);
}
@Test
public void testParseHeader_multipleCouples() {
final String strInit = "[h1]Test1[/h1] [h1]Test2[/h1]";
final String strExpected = "<h1>Test1</h1> <h1>Test2</h1>";
assertThat(parserService.parseHeaders(strInit)).isEqualTo(strExpected);
}
@Test
public void testParseHeaders() {
final String strInit = "[h1]Header1[/h1]\n" +
" Some text in header1\n" +
" [h2]Header2[/h2]\n" +
" Some text in header2\n" +
" [h3]Header3[/h3]\n" +
" Some text in header3\n" +
" [h3][h3]Header3[/h3]\n" +
" Some text in header3\n" +
" [h2]Header2[/h2]\n" +
" Some text in header2\n" +
" [h3]Header3[/h3][/h3]\n" +
" Some text in header3\n" +
" [h1]Header1[/h1]\n" +
" Some text in header1";
final String strExpected = "<h1>Header1</h1>\n" +
" Some text in header1\n" +
" <h2>Header2</h2>\n" +
" Some text in header2\n" +
" <h3>Header3</h3>\n" +
" Some text in header3\n" +
" [h3]<h3>Header3</h3>\n" +
" Some text in header3\n" +
" <h2>Header2</h2>\n" +
" Some text in header2\n" +
" <h3>Header3[/h3]</h3>\n" +
" Some text in header3\n" +
" <h1>Header1</h1>\n" +
" Some text in header1";
assertThat(parserService.parseHeaders(strInit)).isEqualTo(strExpected);
}
/* *********************************************************** */
/* B A C K S P A C E S P A R S I N G */
/* *********************************************************** */
@Test
public void testParseBackSpaces() {
final String strInit = "Hello\nworld!";
final String strExpected = "Hello<br/>world!";
assertThat(parserService.parseBackSpaces(strInit)).isEqualTo(strExpected);
}
@Test
public void testParseBackSpaces_endTagCode() {
final String strInit = "[/code]\n\n";
final String strExpected = "[/code]\n";
assertThat(parserService.parseBackSpaces(strInit)).isEqualTo(strExpected);
}
@Test
public void testParseBackSpaces_endTagCode_2() {
final String strInit = "[/code]\n";
final String strExpected = "[/code]<br/>";
assertThat(parserService.parseBackSpaces(strInit)).isEqualTo(strExpected);
}
/* *********************************************************** */
/* I M A G E S P A R S I N G */
/* *********************************************************** */
@Test
public void testParseImages_simpleImg() {
final String strInit = StringEscapeUtils.escapeHtml("[img src=\"pathToImage\" /]");
final String strExpected = "<img src=\"pathToImage\" alt=\"\" />";
assertThat(parserService.parseImages(strInit)).isEqualTo(strExpected);
}
@Test
public void testParseImages_subTagAlt() {
final String strInit = StringEscapeUtils.escapeHtml("[img src=\"pathToImage\" alt=\"alternative\" /]");
final String strExpected = "<img src=\"pathToImage\" alt=\"alternative\" />";
assertThat(parserService.parseImages(strInit)).isEqualTo(strExpected);
}
@Test
public void testParseImages_multipleImages() {
final String strInit = StringEscapeUtils.escapeHtml("[img src=\"pathToImage1\" /]\n" +
" [img src=\"pathToImage2\" /]");
final String strExpected = "<img src=\"pathToImage1\" alt=\"\" />\n" +
" <img src=\"pathToImage2\" alt=\"\" />";
assertThat(parserService.parseImages(strInit)).isEqualTo(strExpected);
}
@Test
public void testParseImages() {
final String strInit = StringEscapeUtils.escapeHtml("[img src=\"pathToImage1\" /]\n" +
" [img src=\"pathToImage2\" alt=\"alternativeOfImage2\" /]\n" +
" [img src=\"pathToImage3\" lgd=\"legendOfImage3\" /]\n" +
" [img src=\"pathToImage4\" alt=\"alternativeOfImage4\" lgd=\"legendOfImage4\" /]\n" +
" [img src=\"pathToImage5\" \" /]\n" +
" [img src=\"pathToImage6\" src=\"pathToImage6_2\" /]\n" +
" [img src=\"pathToImage7\" alt=\"alternativeOfImage7\" alt=\"alternativeOfImage7_2\" /]\n" +
" [img src=\"pathToImage8\" alt=\"legendOfImage8\" alt=\"legendOfImage8_2\" /]\n" +
" [img alt=\"alternativeOfImage9\" src=\"pathToImage9\" lgd=\"legendOfImage9\" /]\n" +
" [img lgd=\"legendOfImage10\" src=\"pathToImage10\" alt=\"alternativeOfImage10\" /]\n" +
" [img lgd=\"legendOfImage11\" alt=\"alternativeOfImage11\" src=\"pathToImage11\" /]");
final String strExpected = "<img src=\"pathToImage1\" alt=\"\" />\n" +
" <img src=\"pathToImage2\" alt=\"alternativeOfImage2\" />\n" +
" [img src=&quot;pathToImage3&quot; lgd=&quot;legendOfImage3&quot; /]\n" +
" [img src=&quot;pathToImage4&quot; alt=&quot;alternativeOfImage4&quot; lgd=&quot;legendOfImage4&quot; /]\n" +
" [img src=&quot;pathToImage5&quot; &quot; /]\n" +
" [img src=&quot;pathToImage6&quot; src=&quot;pathToImage6_2&quot; /]\n" +
" [img src=&quot;pathToImage7&quot; alt=&quot;alternativeOfImage7&quot; alt=&quot;alternativeOfImage7_2&quot; /]\n" +
" [img src=&quot;pathToImage8&quot; alt=&quot;legendOfImage8&quot; alt=&quot;legendOfImage8_2&quot; /]\n" +
" [img alt=&quot;alternativeOfImage9&quot; src=&quot;pathToImage9&quot; lgd=&quot;legendOfImage9&quot; /]\n" +
" [img lgd=&quot;legendOfImage10&quot; src=&quot;pathToImage10&quot; alt=&quot;alternativeOfImage10&quot; /]\n" +
" [img lgd=&quot;legendOfImage11&quot; alt=&quot;alternativeOfImage11&quot; src=&quot;pathToImage11&quot; /]";
assertThat(parserService.parseImages(strInit)).isEqualTo(strExpected);
}
/* *********************************************************** */
/* L I N K S P A R S I N G */
/* *********************************************************** */
@Test
public void testParseLinks_simpleLink() {
final String strInit = StringEscapeUtils.escapeHtml("[link href=\"pathToLink\" txt=\"textOfLink\" /]");
final String strExpected = "<a href=\"pathToLink\">textOfLink</a>";
assertThat(parserService.parseLinks(strInit)).isEqualTo(strExpected);
}
@Test
public void testParseLinks_multipleLinks() {
final String strInit = StringEscapeUtils.escapeHtml("[link href=\"pathToLink1\" txt=\"textOfLink1\" /]\n" +
" [link href=\"pathToLink2\" txt=\"textOfLink2\" /]");
final String strExpected = "<a href=\"pathToLink1\">textOfLink1</a>\n" +
" <a href=\"pathToLink2\">textOfLink2</a>";
assertThat(parserService.parseLinks(strInit)).isEqualTo(strExpected);
}
/* *********************************************************** */
/* C O D E P A R S I N G */
/* *********************************************************** */
@Test
public void testParseCode_simpleCode() {
final String strInit = parserService.parseBackSpaces(StringEscapeUtils.escapeHtml("[code lg=\"python\"]\n" +
"def foo():\n" +
" return \"Hello world!\"\n" +
"[/code]\n" +
"\n"));
final String strExpected = "<pre class=\"line-numbers\"><code class=\"language-python\">def foo():\n return &quot;Hello world!&quot;\n</code></pre>";
assertThat(parserService.parseCode(strInit)).isEqualTo(strExpected);
}
@Test
public void testParseCode_multipleCodes() {
final String strInit = parserService.parseBackSpaces(StringEscapeUtils.escapeHtml("[code lg=\"python\"]\n" +
"def foo():\n" +
" return \"Hello world!\"\n" +
"[/code]\n" +
"\n" +
"[code lg=\"java\"]\n" +
"public static void main(final String... pArgs) {\n" +
" System.out.println(\"Hello world!\");\n" +
"}\n" +
"[/code]\n" +
"\n"));
final String strExpected = "<pre class=\"line-numbers\"><code class=\"language-python\">def foo():\n return &quot;Hello world!&quot;\n</code></pre><pre class=\"line-numbers\"><code class=\"language-java\">public static void main(final String... pArgs) {\n System.out.println(&quot;Hello world!&quot;);\n}\n</code></pre>";
assertThat(parserService.parseCode(strInit)).isEqualTo(strExpected);
}
@Test
public void testParseLinks_backSlashAndCodeParsing() {
final String strInit = parserService.parseBackSpaces(StringEscapeUtils.escapeHtml("[code lg=\"python\"]\n" +
"def foo():\n" +
" return \"Hello world!\"\n" +
"[/code]\n" +
"\n"));
final String strExpected = "<pre class=\"line-numbers\"><code class=\"language-python\">def foo():\n return &quot;Hello world!&quot;\n</code></pre>";
assertThat(parserService.parseCode(strInit)).isEqualTo(strExpected);
}
@Test
public void testParseLinks_backSlashAndCodeParsing_multiple() {
final String strInit = parserService.parseBackSpaces(StringEscapeUtils.escapeHtml("[code lg=\"python\"]\n" +
"def foo():\n" +
" return \"Hello world!\"\n" +
"[/code]\n" +
"\n" +
"[code lg=\"java\"]\n" +
"public static void main(final String... pArgs) {\n" +
" System.out.println(\"Hello world!\");\n" +
"}\n" +
"[/code]\n" +
"\n"));
final String strExpected = "<pre class=\"line-numbers\"><code class=\"language-python\">def foo():\n return &quot;Hello world!&quot;\n</code></pre><pre class=\"line-numbers\"><code class=\"language-java\">public static void main(final String... pArgs) {\n System.out.println(&quot;Hello world!&quot;);\n}\n</code></pre>";
assertThat(parserService.parseCode(strInit)).isEqualTo(strExpected);
}
@Test
public void testParse() {
final String strInit = "\n" +
"[h1]Title 1[/h1]\n" +
"[h2]Title 2[/h2]\n" +
"[h3]Title 3[/h3]\n" +
"Some text on multiple\n" +
"lines\n" +
"like\n" +
"here!\n" +
"\n" +
"[img src=\"pathToImage\" alt=\"alternativeOfImage\" /]\n" +
"\n" +
"[code lg=\"java\"]\n" +
"public static void main(final String... pArgs) {\n" +
" System.out.println(\"Hello world!\");\n" +
"}\n" +
"[/code]\n" +
"\n" +
"\n" +
"[link href=\"pathToLink1\" txt=\"textOfLink1\" /]\n" +
"\n" +
"[img src=\"pathToImage2\" alt=\"alternativeOfImage2\" lgd=\"legendOfImage2\" /]\n" +
"[link href=\"pathToLink2\" txt=\"textOfLink2\" /]\n" +
"\n" +
"[code lg=\"python\"]\n" +
"def foo():\n" +
" return \"Hello world!\"\n" +
"[/code]\n" +
"\n" +
"";
final String strExpected = "<br/>" +
"<h1>Title 1</h1><br/>" +
"<h2>Title 2</h2><br/>" +
"<h3>Title 3</h3><br/>" +
"Some text on multiple<br/>" +
"lines<br/>" +
"like<br/>" +
"here!<br/>" +
"<br/>" +
"<img src=\"pathToImage\" alt=\"alternativeOfImage\" /><br/>" +
"<br/>" +
"<pre class=\"line-numbers\"><code class=\"language-java\">" +
"public static void main(final String... pArgs) {\n" +
" System.out.println(&quot;Hello world!&quot;);\n" +
"}\n" +
"</code></pre><br/>" +
"<a href=\"pathToLink1\">textOfLink1</a><br/>" +
"<br/>" +
"[img src=&quot;pathToImage2&quot; alt=&quot;alternativeOfImage2&quot; lgd=&quot;legendOfImage2&quot; /]<br/>" +
"<a href=\"pathToLink2\">textOfLink2</a><br/>" +
"<br/>" +
"<pre class=\"line-numbers\"><code class=\"language-python\">" +
"def foo():\n" +
" return &quot;Hello world!&quot;\n" +
"</code></pre>";
assertThat(parserService.parse(strInit)).isEqualTo(strExpected);
}
}