Angular Protactor test getting h1 text from html - html

I am very new in end to end testing. In my app I have a login page, which I want to show the user when they logout from the app. Now there is a text h1 inside the div. But I am not getting the text from that div and that is why the expected result is different from the actual result.
Here is my login page html.
<div *ngIf="!isLoggedIn" class="login-controller">
<div layout="column" class="login-dialog">
<h1>Here is a heading</h1>
<h2>Second Heading</h2>
<div class="border">
...
</div>
</div>
</div>
Here is my app.po.ts
async getLogInPage(){
return await element(by.css('h1')).getText();
}
async logoutOfApplication() {
var userMenu = element(by.css(".prof-dropbtn > span"));
browser.wait(ExpectedConditions.presenceOf(userMenu), 10000);
await userMenu.click();
var logoutButton = element(by.id("logout"));
await logoutButton.click();
}
Now app.e2e-spec.ts
it('Test for logout', () => {
page.logoutOfApplication();
expect(page.getLogInPage()).toEqual('Here is a heading');
page.loginToApplication("email.com", "demo");
});

If you want get h1 value you have to write like this
it('Test for logout', () => {
page.logoutOfApplication();
expect(element.all(by.css('.login-dialog h1')).getText()).toEqual('Here is a heading');
page.loginToApplication("eamil.com", "demo");
});

Related

Fetch content from MYSQL database not showing line breaks (MYSQL, SEQUALIZE, NODE, HANDLEBARS)

Using a database management tool (HeidiSQL) I can see that the content of an entry is storing returns (good):
MYSQL storing line breaks
However when I read the data on my front-end:
router.get('/story/:id', async (req, res) => {
try {
const getStory = await Story.findByPk(req.params.id, {
include: [
{
model: User,
attributes: ['username'],
},
],
});
const story = getStory.get({ plain: true });
res.render('story', {
story,
logged_in: req.session.logged_in,
});
} catch (err) {
res.status(500).json(err);
}
});
Rendered in Handlebars:
<div class="card">
<div class="card-content">
<p class="title">
{{story.title}}
</p>
<p class="content">
{{story.content}}
</p>
</div>
</div>
It eliminates the line-breaks:
no line-breaks
I'm wondering what I need to do to maintain the linebreaks.
I haven't tried modifying anything yet. I will try encapsulating the handlebars {{story.content}} in a string-literal to see if that does it.
So I found the answer - I needed to add a custom handlebars helper in the server.js
hbs.handlebars.registerHelper('breaklines', function(text) {
text = hbs.handlebars.Utils.escapeExpression(text);
text = text.replace(/(\r\n|\n|\r)/gm, '<br>');
return new hbs.handlebars.SafeString(text);
});
Then pass the content through the helper
<div class="card">
<div class="card-content">
<p class="title">
{{story.title}}
</p>
<p class="content">
{{breaklines story.content}}
</p>
</div>
</div>

How to make custom slide change button in Swiper? (Vue.js)

I need to delete default navigation (nextEl, prevEl) and make button with "NEXT SLIDE BUTTON" text scroll to next slide
<swiper class="news-slider" :sliderd-per-view="1" :modules="[Navigation, Pagination, A11y, Lazy]" navigation="navigation" :navigation="{ nextEl: '.nextArrow'}" :pagination="{ clickable: true, dynamicBullets: true, dynamicMainBullets: 5 }" preload-images="false" lazy="lazy">
<swiperSlide class="swiper-slide" v-for="n in 10" :key="n">
<div class="news-container">
<div class="news"><img class="news__image swiper-lazy" src="#/assets/img/lookism-transformed-transformed.png" alt=""/>
<div class="swiper-lazy-preloader"></div>
<div class="news__info">
<div class="news__title">title</div>
<div class="news__description">description</div>
<div class="news__buttons">
<button class="normal-button news-button">more</button>
<button class="next-button"><span class="next-button__text">NEXT SLIDE BUTTON</span><img class="next-button__icon" src="#/assets/img/arrow-right.svg" alt=""/></button>
</div>
</div>
</div>
</div>
</swiperSlide>
</swiper>
<script>
export default {
components: {
Swiper,
SwiperSlide,
},
setup() {
const onSwiper = (swiper) => {
console.log(swiper);
};
const onSlideChange = () => {
console.log('slide change');
};
return {
onSwiper,
onSlideChange,
modules: [Navigation, Pagination, Scrollbar, A11y],
};
},
};
</script>
How can i do that? I will be very grateful for your help
This should do it, all you need to do is import useSwiper and assign it to a const variable and use it in the div element as swiper.slideNext()
And also if you do not wish to use swiper's own elements then remove the navigation module.
Here's the doc source which is clearer
https://swiperjs.com/vue#use-swiper

More efficient way to create an array of objects using puppeteer

I'm trying to scrape a page that contains a bunch of text messages. The messages are arrange in a similar manner to the example below. I would like to use puppeeter to create an array of objects. Each object would contain the inner text of each message excluding one of the elements.
The array I would like to build should look like:
const messages = [{name: 'Greg', textMessage: 'Blah Blah Blah'}, {name: 'James', textMessage: 'Blah Blah Blah'},{name: 'Sam', textMessage: 'Blah Blah Blah'}]
Example: HTML markup
<div class="messages">
<div class="message">
<a class="name">Greg</a>
<p class="element-you-dont-want">Don't scrape this</p>
<p class="textMessage">Blah Blah Blah</p>
</div>
<div class="message">
<a class="name">James</a>
<p class="element-you-dont-want">Don't scrape this</p>
<p class="textMessage">Blah Blah Blah</p>
</div>
<div class="message">
<a class="name">Sam</a>
<p class="element-you-dont-want">Don't scrape this</p>
<p class="textMessage">Blah Blah Blah</p>
</div>
</div>
My current code creates two arrays, one for names the other for the textMessages, then I have to combine them. Is there a more efficient way to do this.
const names = await page.evaluate(
() => Array.from(document.querySelectorAll("div.messages a.name")).map(name => name.innerText)
);
const textMessages = await page.evaluate(
() => Array.from(document.querySelectorAll("div.messages p.textMessage")).map(textMessage => textMessage.innerText)
);
... From here I combine the two into an object of arrays.
There is an $$eval function in Page, which translates to Array.from(document.querySelectorAll(selector)) within the context and passes it as the first argument to pageFunction.
Usage:
const result = await page.$$eval('div.message', (msgs) => msgs.map((msg) => {
return {
name: msg.querySelector('a.name').innerText,
textMessage: msg.querySelector('a.textMessage').innerText
}})
);
You can scrape them together,
page.evaluate(() => {
const messages = [...document.querySelectorAll("div.message")]; // notice this is not .messages
return messages.map(message => {
return {
name: message.querySelector('a.name').innerText,
textMessage: message.querySelector('a.textMessage').innerText
}
}
}
});

Passing Object in ng-repeat to another page to display its contents

I am working on a project using MySQL, Angular, Express, and Node. I have a list of objects in a ng-repeat and when I click a specific item I would like to pass the clicked object to another page and show the object's properties through angular.
Here is the code:
HTML:
<div class = "panel panel-info" ng-repeat="job in job">
<div class = "panel-heading clickable">
<h1 class = "panel-title">{{job.title}}</h1>
<span class = "pull-right"><i class = "glyphicon glyphicon-minus"></i></span>
</div>
<div class = "panel-body">
<!--This will soon be the place where the Students information is placed by NodeJS-->
<!--<p style = "text-decoration: underline"> Job Title <p>-->
<p> {{job.description}} </p>
<p> {{job.first_name}} {{job.last_name}}</p>
<p> {{job.location}} </p>
<br>
<div class="form-group">
<div class=" col-sm-15">
<button onclick="location.href='jobPage.html';" type="submit" class="btn btn-default btn-block">Apply</button>
</div>
</div>
</div>
</div>
Controller:
soopControllers.controller("landingController",
function ($scope, $http){
$scope.formData = {};
$http.get('/api/jobLanding')
.success(function(data){
$scope.job = data;
console.log(data);
})
.error(function(data){
console.log('Error: ' + data);
});
//I want this function to get the job and send it to another page
$scope.getJob = function(){
$http.post('/api/job', $scope.formData)
.success(function(data){
$scope.formData = {};
$scope.users = data;
//$location.redirect();
console.log(data);
})
.error(function(data){
console.log('Error: ' + data);
});
};
});
AngularJS applications work the same way as regular web sites when it comes to navigation. The difference is that instead of sending a request to the server to go to the new URL, the router intercepts the location change, and goes to a route. The controller (or the resolve function) of that route then gets what it needs to display.
So, what you need in your ng-repeat instead of your button is
<a ng-href="#/job/{{ job.id }}">Apply</a>
And you then need a route mapped to the path /job/:jobId.
In the controller of this route, you'll then do something like
$http.get('/api/job/' + $routeParams.jobId).then(function(response) {
$scope.job = response.data;
});
How about using ng-click on the repeated element and extract that element in your display/routed page.
<div ng-controller="plandingController"
class = "panel panel-info"
ng-repeat="job in job"
ng-click="value.val=job">
....
</div>
In jobPage.html
<div ng-controller="plandingController" ng-repeat="pickedjob in value.val">

Show/Hide onclick - angularjs

This is what i'm trying to achieve.
section1 will be hidden at page load. When user clicks on Advanced, section1 should show & section2 should hide. On clicking Basic, the opposite should happen. Nothing happens when I click any of the anchor tags now. Where am i going wrong.
<div class="container" ng-init="advstatus=true">
Basic
<br/>
Advanced
<div class="section1" ng-hide="advstatus">
///...
</div>
<section ng-show="advstatus" class="section2">
///...
</section>
</div>
In AngularJS you need to use ng-click instead of onclick.
Also, ng-init isn't supposed to be used unless you're in ng-repeat, which you are not (take a look at the Docs).
You should set up the variable in your controller:
<div ng-app ng-controller="myCtrl" class="container" >
Basic
<br/>
Advanced
<div class="section1" ng-show="!advstatus">
Section 1
</div>
<section ng-show="advstatus" class="section2">
Section 2
</section>
</div>
Controller:
function myCtrl($scope) {
$scope.advstatus = true;
}
JS Fiddle
This one is easy to understand
<div class="section1" ng-show="state1">
Section 1
</div>
<div class="section2" ng-show="state2">
Section 2
</div>
<input type="button" value="Advance" ng-click="sec1_show()" />
<input type="button" value="Basic" ng-click="sec2_show()" />
<script>
function homecntrl($scope) {
$scope.state1 = false;
$scope.state2 = true;
$scope.sec1_show = function () {
$scope.state1 = true;
$scope.state2 = false;
};
$scope.sec2_show = function () {
$scope.state2 = true;
$scope.state1 = false
};
};
</script>
Very simple just do this:
<button ng-click="hideShow=(hideShow ? false : true)">Toggle</button>
<div ng-if="hideShow">hide and show content ...</div>