Actually I am testing in browser directly and I am getting error saying no element found using locator.
spec.js
it('Test for Application Registration text', function () {
var ar = browser.findElement(by.xpath('/html/body/admin-app-root/layout/div[1]/cheader/nav/div/div[1]/a[2]'));
expect(ar.getAttribute('value')).toEqual('Application Registration');
});
HTML:
<div _ngcontent-bbu-25="" class="navbar-header">
<a _ngcontent-bbu-25="" class="navbar-brand logo navbar-brand-logo" href="/register/core/feature-list"></a>
<a _ngcontent-bbu-25="" class="navbar-brand navbar-brand-title app-title ellipses" href="/register/core/feature-list">Application Registration</a>
</div>
Error:
Message:[chrome #01] Failed: no such element: Unable to locate element:
{"method":"xpath","selector":"/html/body/adminapproot/layout/div[1]/
cheader/nav/div/div[1]/a[2]"}[chrome #01]
(Session info: chrome=61.0.3163.100)[chrome #01]
(Driver info:chromedriver=2.32.498550(9dec58e66c31bcc53a9ce3c7226f0c1c5810906a),
platfor=Windows NT 10.0.14393 x86_64)[chrome #01]
Stack:[chrome #01] NoSuchElementError: no such element: Unable to locate
element:{"method":"xpath","selector":"/html/body/admin-app-
root/layout/div[1]/cheader/nav/div/div[1]/a[2]"}[chrome #01]
(Session info: chrome=61.0.3163.100)[chrome #01]
(Driver info:chromedriver=2.32.498550(9dec58e66c31bcc53a9ce3c7226f0c1c5810906a),
platfor=Windows NT 10.0.14393 x86_64)[chrome #01]
at WebDriverError(C:\\node_modules\proractor\node_modules\selenium-
webdriver\lib\error.js:27:5)
This works fine with this code:
it('Test for Application Registration text', function () {
var EC = protractor.ExpectedConditions;
var ar = element(by.xpath('/html/body/admin-app-root/layout/div[1]/c-header/nav/div/div[1]/a[2]'));
browser.wait(EC.visibilityOf(ar));
expect(ar.getAttribute('value')).toEqual('Application Registration');
});
Related
I am having trouble with testing my oauth-secured application.
The problem manifests itself when there is no public page - user is immediately redirected to OAuth server it they are not authenticated.
I managed to reproduce the problem in much simpler setup:
fake app running in fake-app domain
fake oauth server running in fake-oauth-server domain
Here are respective apps (in Flask):
Fake app
from flask import Flask, redirect, render_template_string
app = Flask(__name__)
app_host="fake-app"
app_port=5000
app_uri=f"http://{app_host}:{app_port}"
oauth_host="fake-oauth-server"
oauth_port=5001
oauth_uri=f"http://{oauth_host}:{oauth_port}"
#app.route('/')
def hello():
return render_template_string('''<!doctype html>
<html>
<body>
<p>Hello, World MainApp!</p>
<a id="loginButton" href="{{ oauth_uri }}?redirect_uri={{ app_uri }}">Login</a>
</body>
</html>
''',
oauth_uri=oauth_uri,
app_uri=app_uri
)
#app.route('/goto-oauth')
def goto_oauth():
return redirect(f"{oauth_uri}?redirect_uri={app_uri}")
if __name__ == '__main__':
app.run(host=app_host, port=app_port)
Fake oauth server:
from flask import Flask, render_template_string, request
app = Flask(__name__)
oauth_host="fake-oauth-server"
oauth_port=5001
#app.route('/')
def login():
return render_template_string(
'''<!doctype html>
<html>
<body>
<p>Please log in</p>
<label>Username: <label><input id="username" />
<label>Password: <label><input id="password" />
<a id="submit-password" href="{{ redirect_uri }}">Submit</a>
</body>
</html>
''', redirect_uri=request.args.get('redirect_uri'))
if __name__ == '__main__':
app.run(host=oauth_host, port=oauth_port)
First flow: there is a publicly available page with Login button
This is possible to test with cy.origin:
describe('My Scenarios', () => {
beforeEach(() => {
cy.visit('/');
cy.contains('MainApp');
cy.get('a#loginButton').click();
cy.origin('http://fake-oauth-server:5001', () => {
cy.contains('Please log in');
cy.get('input#username').type('user1');
cy.get('input#password').type('password1');
cy.get('a#submit-password').click()
});
});
it.only('test flask', () => {
cy.visit('/');
cy.contains('MainApp');
});
});
Problematic flow: immediate redirect to Oauth server
describe('My Scenarios', () => {
beforeEach(() => {
cy.visit('/goto-oauth');
cy.origin('http://fake-oauth-server:5001', () => {
cy.contains('Please log in');
cy.get('input#username').type('user1');
cy.get('input#password').type('password1');
cy.get('a#submit-password').click()
});
});
it.only('test flask', () => {
cy.visit('/');
cy.contains('MainApp');
});
});
Fails with:
CypressError: `cy.origin()` requires the first argument to be a different domain than top. You passed `http://fake-oauth-server:5001` to the origin command, while top is at `http://fake-oauth-server:5001`.
Either the intended page was not visited prior to running the cy.origin block or the cy.origin block may not be needed at all.
There is no publicly available page in my app - how can I amend the test to make it work?
It seems to work if visit the redirect URL inside the cy.origin().
I set the app on http://localhost:6001 and the auth server on http://localhost:6003, using express rather than flask.
Test
describe('My Scenarios', () => {
beforeEach(() => {
cy.origin('http://localhost:6003', () => {
cy.visit('http://localhost:6001/goto-oauth')
cy.contains('Please log in');
cy.get('input#username').type('user1');
cy.get('input#password').type('password1');
cy.get('a#submit-password').click()
});
});
it('test main app', () => {
cy.visit('http://localhost:6001')
cy.contains('MainApp')
})
})
App
const express = require('express')
function makeApp() {
const app = express()
app.get('/', function (req, res) {
res.send(`
<html>
<body>
<p>Hello, World MainApp!</p>
<a id="loginButton" href="http://localhost:6003?redirect_uri=http://localhost:6001">
Login
</a>
</body>
</html>
`)
})
app.get('/goto-oauth', function (req, res) {
res.redirect('http://localhost:6003')
})
const port = 6001
return new Promise((resolve) => {
const server = app.listen(port, function () {
const port = server.address().port
console.log('Example app listening at port %d', port)
// close the server
const close = () => {
return new Promise((resolve) => {
console.log('closing server')
server.close(resolve)
})
}
resolve({ server, port, close })
})
})
}
module.exports = makeApp
Auth
const express = require('express')
function makeServer() {
const app = express()
app.get('/', function (req, res) {
res.send(`
<!doctype html>
<html>
<body>
<p>Please log in</p>
<label>Username: <label><input id="username" />
<label>Password: <label><input id="password" />
<a id="submit-password" href="http://localhost:6001">Submit</a>
</body>
</html>
`)
})
const port = 6003
return new Promise((resolve) => {
const server = app.listen(port, function () {
const port = server.address().port
console.log('Example app listening at port %d', port)
// close the server
const close = () => {
return new Promise((resolve) => {
console.log('closing server')
server.close(resolve)
})
}
resolve({ server, port, close })
})
})
}
module.exports = makeServer
I am accessing my Gmail account by command loginByGoogleApi under commands.js, then getting the requested email body and pulling the required data from the body, and saving it in a const variable code typing the pulled email data (code) in my testFile.cy.js.
I am able to login after this but cypress throwing me an error cy.type() failed because it targeted a disabled element.
commands.js
/// <reference types="cypress"
import { parseString } from "xml2js";
Cypress.Commands.add('loginByGoogleApi', () => {
cy.request({
method: 'POST',
url: 'https://www.googleapis.com/oauth2/v4/token',
body: {
grant_type: 'refresh_token',
client_id: Cypress.env('googleClientId'),
client_secret: Cypress.env('googleClientSecret'),
refresh_token: Cypress.env('googleRefreshToken'),
},
}).then(({ body }) => {
const { access_token, id_token } = body
cy.log('Opening emails including code to verify')
cy.request({
method: 'GET',
url: 'https://mail.google.com/mail/feed/atom/verifyCode',
headers: { Authorization: Bearer ${access_token} },
}).then(({ body }) => {
parseString(body, function (err, results) {
let data = JSON.stringify(results)
let codeTitle = JSON.parse(data).feed.entry[0].title[0];
let code = codeTitle.replace('Chaine confirmation code: ','');
cy.log(code)
});
});
})
})
testFile.cy.js
const { Code } = require("#chaine/keychaine");
describe('Open login page', () => {
it('Enter your email to login', () => {
cy.visit('https://chaineapp.com/staging/login%27)
cy.get('#field-1').click().type('paras#loadtap.com');
cy.get('[class*="chakra-button css-yg51i0"]').click();
cy.get('#pin-input-2-0').type(<need to put code here>);
})
it('get code', () => {
cy.loginByGoogleApi()
})
})
CypressError
cy.type() failed because it targeted a disabled element.
The element typed into was:
<input aria-label="Please enter your pin code" inputmode="numeric" type="tel" id="pin-input-2-5" autocomplete="one-time-code" placeholder="" class="chakra-pin-input css-jyb0wy" value="1" data-index="5" disabled="">
Ensure the element does not have an attribute named disabled before typing into it.
image of error
You can add force: true with type to disable actionability checks -
cy.get('#pin-input-2-0').type('text to type', {force: true});
"{"post
_0":["blob"],"q":["[{"app_id":"936619743392459","posts":"0","user":"17841452298923592","webSessionId":":xsehw9:s7q6v3","send_method":"beacon","compression":"snappy","snappy_ms":8}]"],"ts":["1656260390276"]}",
The digits are entered into separate inputs and the cursor is moved right after each character, but the Cypress .type() command is entering the digits faster than the cursor can move.
Add a delay to the type command.
cy.get('#pin-input-2-0')
.type(code, {delay:100})
Version info
Angular CLI: 6.0.8
Node: 8.11.2
OS: win32 x64
Angular: 6.0.3
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router
Package Version
-----------------------------------------------------------
#angular-devkit/architect 0.6.3
#angular-devkit/build-angular 0.6.3
#angular-devkit/build-optimizer 0.6.3
#angular-devkit/core 0.6.3
#angular-devkit/schematics 0.6.8
#angular/cdk 6.1.0
#angular/cli 6.0.8
#angular/flex-layout 6.0.0-beta.17
#angular/material 6.1.0
#ngtools/webpack 6.0.3
#schematics/angular 0.6.8
#schematics/update 0.6.8
rxjs 6.2.0
typescript 2.7.2
webpack 4.8.3
Protractor: 5.4.0
jasmine-core: 2.6.2
jasmine-spec-reporter: 4.1.0
Problem
I wanted to make a simple e2e test to check the inner text of an element after the browser has redirected to a page. However, when checking inside of the element, the browser simply sits on the page for 10 seconds and then gives the following error:
1) material App should re-route to login page
- Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
- ScriptTimeoutError: Timed out waiting for asynchronous Angular tasks to finish after 10 seconds. This may be because the current page is not an Angular application. Please see the FAQ for more details: htt
ps://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular
While waiting for element with locator - Locator: By(css selector, #globTitle)
Executed 1 of 1 spec (1 FAILED) in 14 secs.
[20:04:24] I/launcher - 0 instance(s) of WebDriver still running
[20:04:24] I/launcher - chrome #01 failed 1 test(s)
[20:04:24] I/launcher - overall: 1 failed spec(s)
[20:04:24] E/launcher - Process exited with error code 1
An unexpected error occured: undefined
Here is my code:
HTML:
<div class="main-container">
<mat-toolbar [ngStyle]="{'background-color': topBarColor, 'color': 'white'}" class="topbar telative" style = "z-index: 0 !important;">
<button mat-icon-button (click)="snav.toggle()" *ngIf = "showbtn" value="sidebarclosed">
<mat-icon>menu</mat-icon>
</button>
<span id = "globTitle">Programme Admin Dashboard</span>
<span style = "flex: 1 1 auto;"></span>
<button mat-raised-button colour = "warn" *ngIf = "showbtn" (click) = "signOut(); snav.close();" style = "margin-right: 20px;">Sign out</button>
<img src="assets/images/logo.png" alt="No logo available" style = "height: inherit; margin-bottom: 5px;">
</mat-toolbar>
<mat-sidenav-container class="example-sidenav-container" [style.marginTop.px]="mobileQuery.matches ? 0 : 0">
<mat-sidenav style = "text-align: center;" #snav id="snav" class="dark-sidebar pl-xs" [mode]="mobileQuery.matches ? 'side' : 'over'" fixedTopGap="0" [opened]= false [disableClose]="mobileQuery.matches" >
<app-sidebar (click) = "snav.close()"></app-sidebar>
</mat-sidenav>
<mat-sidenav-content class="page-wrapper">
<div class="page-content">
<router-outlet><app-spinner></app-spinner></router-outlet>
</div>
</mat-sidenav-content>
<ng-template #loadingSpinner>
<mat-spinner></mat-spinner>
<p>Fetching permissions...</p>
</ng-template>
</mat-sidenav-container>
</div>
app.po.ts:
import {browser, by, element, protractor} from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getURIChange(url: string) {
const ec = protractor.ExpectedConditions;
browser.wait(ec.urlContains(url), 2000);
}
}
app.e2e-spec.ts:
(Note that when i remove the last line of code, the test manages to run. In fact, even up to element(by.css('#globTitle')) works fine.
But if i change it to element(by.css('#globTitle')).getText(), this is where the browser just waits and i get the error mentioned above.
import { AppPage } from './app.po';
import {browser, element, by} from 'protractor';
describe('material App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should re-route to login page', async () => {
console.log('Waiting for angular');
await browser.waitForAngularEnabled();
console.log('Angular initialized');
console.log('Waiting on navigation to page');
// page.navigateTo();
await browser.get('http://localhost:4201/');
console.log('Navigation completed, should be redirected to the login page');
page.getURIChange('/login');
expect(element(by.css('#globTitle')).getText()).toEqual('Programme Admin Dashboard');
});
});
Also, i'm not sure if this would help, but here are my protractor configuration and karma configurations just in case.
protractor.conf.js:
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 10000,
specs: [
'./e2e/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 10000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: 'e2e/tsconfig.e2e.json'
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
karma.conf.js:
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '#angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('#angular-devkit/build-angular/plugins/karma')
],
client:{
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
fixWebpackSourcePaths: true
},
angularCli: {
environment: 'dev'
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
customLaunchers: {
ChromeNoSandbox: {
base: 'Chrome',
flags: ['--no-sandbox']
}
},
singleRun: false,
});
};
Try this :
const el1 = await $('#globTitle').getText();
expect(el1).toEqual('Programme Admin Dashboard');
getText() waits for a promise to resolve and hence doesn't work the way it was before.
Hello you are not returning the callback in your step definitions.
So jasmine thinks you are doing or waiting for some actions to happen.But once timeout occured that is 10 seconds it throws an error
it('should re-route to login page', async (done) => {
console.log('Waiting for angular');
await browser.waitForAngularEnabled();
console.log('Angular initialized');
console.log('Waiting on navigation to page'); // page.navigateTo();
await browser.get('http://localhost:4201/');
console.log('Navigation completed, should be redirected to the login page');
page.getURIChange('/login');
Var a = element(by.css('#globTitle');
Var b = a.getText().then((value)=>{console.log('title is'+value);
return value;})
expect(b).toEqual('Programme Admin Dashboard'); done()
});
I have Bootstrap 2 app that displays a SVG, when I click on any path element I get the Bootstrap 2 popover as expected.
$('body').popover
'selector': 'path'
'container': 'body'
'html': true
'width': '360px'
'title': () ->
item = LOOKUP[this.id]
'' + item['device'] + '/' + item['physical_port'] + '' + '<button type="button" id="close" class="close">×</button>'
'content': () ->
this.id
item = LOOKUP[this.id]
url = '/graphs.horizon/utilisation/'+item['device']+'/'+item['physical_port']+'?layout=naked'
'<iframe src="'+url+'" frameBorder="0" width="360px" height="180px"></iframe>'
'trigger': 'click'
'placement': 'right'
So I naively add a general click event like the following:
$(document).on 'click', '.popover .close', () ->
console.log 'hello %o', $(this).parents('.popover')
$(this).parents('.popover').popover('hide')
However, it doesn't seem to work. I get the debug hello message, however the .popover('hide') doesn't seem to do anything except print the debug message.
In the console I also try $('.popover').popover('hide') and that doesn't do anything either but using just the selector shows a list of the div objects as I would expect.
this worked:
$(document).on 'click', '.popover .close', () ->
console.log 'hello %o', $(this).parents('.popover')
$(this).parents('.popover').hide()
I use dropzone with CI, i don't know how to display error message and custom message when upload false, this is my script
Dropzone.autoDiscover = false;
try {
var myDropzone = new Dropzone("#adminform" , {
paramName: "filename", // The name that will be used to transfer the file
maxFilesize: 0.5, // MB
url: window.location.href,
addRemoveLinks : true,
dictDefaultMessage :
'<span class="bigger-150 bolder"><i class="ace-icon fa fa-caret-right red"></i> Drop files</span> to upload \
<span class="smaller-80 grey">(or click)</span> <br /> \
<i class="upload-icon ace-icon fa fa-cloud-upload blue fa-3x"></i>',
dictResponseError: 'Error while uploading file!',
//change the previewTemplate to use Bootstrap progress bars
previewTemplate: "<div class=\"dz-preview dz-file-preview\">\n <div class=\"dz-details\">\n <div class=\"dz-filename\"><span data-dz-name></span></div>\n <div class=\"dz-size\" data-dz-size></div>\n <img data-dz-thumbnail />\n </div>\n <div class=\"progress progress-small progress-striped active\"><div class=\"progress-bar progress-bar-success\" data-dz-uploadprogress></div></div>\n <div class=\"dz-success-mark\"><span></span></div>\n <div class=\"dz-error-mark\"><span></span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
});
}
catch(e) {
alert('Dropzone does not support older browsers!');
}
And PHP return 400:
$this->output->set_header("HTTP/1.0 400 Bad Request");
But when i hover image it's display [object Object] but message is:
dictResponseError: 'Error while uploading file!'
For anyone in need:
You can return a response message from the server using echo. Then in the js code add an error event handler
PHP
header("HTTP/1.0 400 Bad Request");
echo "Ups error message";
JS
this.on('error', function(file, response) {
$(file.previewElement).find('.dz-error-message').text(response);
});
For me this code finally worked, used as a dropzone option:
error: function(file, message) {
$(file.previewElement).addClass("dz-error").find('.dz-error-message').text(message.Message);
}
I used message.Message since the ASP.net WebAPI returns a JSON object, but not with the required "error" key.
You can simply echo back the message from server via PHP file
if($file_uploaded == true)
{
//perform operations on valid upload
} else {
//upload failed, echo back negative response to dropzone.js
$this->output->set_header("HTTP/1.0 400 Bad Request");
echo "Error uploading file";
}
While your HTML file can look like:
<script type="text/javascript">
Dropzone.options.myAwesomeDropzone = {
paramName: "icon_image", // The name that will be used to transfer the file
maxFilesize: 2, // MB
init: function() {
this.on("error", function(file, response) {
// do stuff here.
alert(response);
});
}
};
</script>
Hope it helps :)