I'm adding tests to an application that already (partially) exists. It was written using angular and php/MariaDB in the backend. I'm now working on the http calls to the server. I plan to use jasmine with request.
I was able to make some simple tests, and can login. But I cannot test the pages that require to be logged in. I cannot find a way to add the token to the calls.
If I understand things correctly, on the received message from the sig in I should get a token that I should then use in the following calls. Who wrote the app followed the instructions given by the angular documentation which handles everything, so we are learning toguether how things really work under the hood.
Going through the received answer on the login, the only thing that looks like a token is a cookie set in the header, whose name is 'PHPSESSID'. I read and parse that cookie to get the token and make the next call like this:
request.get(
{
url: 'http://xxx.xxx.com/php/authentication/session.php',
'auth': {
'bearer': mytoken
}
}, function(err, res) {
console.log(res['body']);
done();
})
the response is what I should get if the user is NOT logged in.
Using Postman, everything works. Aparently it saves the token and uses it for the next call. That is, f I make the sign in and then make the get call to session.php I get the correct answer. I just cannot figure out what exact call postman makes or how to use the token in the next call using jasmine and request.
Since the token was passed as a cookie on the response call, what I was supposed to do was set the same cookie in the next call. Here is the code of the whole test just in case somebody needs a hand. First I sign in, then I made a call to an address that should return my email if indeed I'm logged in.
var request = require('request');
describe("login test", function() {
it("should log in", (done) => {
var user = {'email':'test#xxx.de', 'password':'blablabla'};
request.post(
{
url: 'http://xxx/test/public/php/authentication/login.php',
body: JSON.stringify(user)
},
(err, res) => {
var result = JSON.parse(res['body']);
var cookieString = res['headers']['set-cookie'];
expect(result['success']).toBe(true);
request.get(
{
url: 'http://xxx/test/public/php/authentication/session.php',
headers: {
'Cookie': cookieString
}
}, function(err, res) {
var result = JSON.parse(res['body']);
expect(result.user.email).toBe(user.email);
done();
})
});
});
});
Related
I have the following Node.JS (ran with Express) code :
let app = express();
app.use(cors());
app.get('/callback', function (req, res) {
// your application requests refresh and access tokens
// after checking the state parameter
var code = req.query.code || null;
var authOptions = {
url: 'https://accounts.spotify.com/api/token',
form: {
code: code,
redirect_uri: redirectUri,
grant_type: 'authorization_code'
},
headers: {
'Authorization': 'Basic ' + (new Buffer(clientId + ':' + clientSecret).toString('base64'))
},
json: true
};
request.post(authOptions, function (error, response, body) {
if (!error && response.statusCode === 200) {
var access_token = body.access_token,
refresh_token = body.refresh_token;
fs.writeFile('test.txt', 'HELLO', function (err) {
if (err) return console.log(err);
console.log('Hello World > helloworld.txt');
});
}
}
)
});
console.log('Listening on 8888');
app.listen(8888);
The route is used as a callback for a request to the Spotify Web API, thus I can get an access token.
Spotify then redirects to the callback function above, you can see it in the URI by looking at "redirect_uri".
If you need more information about the Spotify Authorization Flow, see here.
Here's the URI I'm using to authenticate my app to Spotify.
https://accounts.spotify.com/authorize?client_id=CLIENT_ID&response_type=code&redirect_uri=http://localhost:8888/callback&scope=user-read-private%20user-read-email%20playlist-modify-public&state=PexBrjEzISHepTp7&show_dialog=false
CLIENT_ID is replaced by my real CLIENT_ID in the request I make
My problem is located to the file writing part :
fs.writeFile('test.txt', 'HELLO', function (err) {
if (err) return console.log(err);
console.log('Hello World > helloworld.txt');
});
When the callback route is called by Spotify, I have the string "HELLO" wrote in my text file, so the file writing is functional.
But even if it has finished writing the string, the Chrome page is still running and "pending" on the server. It runs for a few minutes and then crash by saying that the page didn't sent any data. Why ?
I've looked at this page talking about the methods of writing to text files, using writeFile and writeFileAsync, but using both of them didn't solved my problem.
EDIT: I don't really want to stop the Express process! I just want to be able to process another request :)
Any idea ? Thanks in advance :)
You aren't returning anything from your route, try adding res.send({})
In your get route you are not sending response, you must send response irrespective of writing a file was successful or not.
Add below code post writing to file (as well as in if error case)
res.send({YOUR_CHOICE_RESPONSE_DATA})
I'm making a webpage with Node JS with dustjs and PostgreSQL. How do I make a search query in the html, so I can pass the value to the app.get
Do I need to use JQuery?
app.get('/teachers', function(req, res){
pool.connect(function(err, client, done){
if(err) {
return console.error("error", err);
}
client.query('SELECT * FROM teachers', function(err, result){
if(err){
return console.error('error running query', err)
}
res.render('teacherindex', {teachers: result.rows});
done();
});
});
});
app.get('/teachers/:str', (req,res)=>{
pool.connect((err, client, done) => {
if (err) throw err
client.query('SELECT * FROM teachers WHERE name = $1', [req.query.namesearch], (err, result) => {
done()
if (err) {
console.log(err.stack)
} else {
res.render('teacherindex', {teachers: result.rows});
}
})
})
})
This is my JQuery
$("#myBtn").click(function(){
var str = $("#myInput").val();
var url = '/teachers/'+str;
if(confirm('Search Record?')){
$.ajax({
url: url,
type: 'put',
success: function(result){
console.log('Searching');
window.location.href='/teachers';
},
error: function(err){
console.log(err);
}
});
}
});
My HTML
<input type="text" id="myInput" data-id="namesearch">
<button type="button" id="myBtn">Show Value</button>
Thank you!
FINAL ANSWER:
Ok so it turns out the issue you were having was something completely different. You are trying to use server side rendering for this, and I was showing you how to render the retrieved data on the client side.
I have forked, and updated your repo - which can be found at the link below..
Please review my changes and let me know if you have any questions.
Working repo: https://github.com/oze4/hanstanawi.github.io
Demo Video: https://raw.githubusercontent.com/oze4/hanstanawi.github.io/master/fake_uni_demo.mp4
EDIT:
I went ahead and built a repository to try and help you grasp these concepts. You can find the repo here - I tried to keep things as simple and understandable as possible, but let me know if you have any questions.
I had to make some minor changes to the paths, which I have commented explanations on the code in the repo.
I am using a "mock" database (just a JSON object in a different file) but the logic remains the same.
The index.js is the main entry point and contains all route data.
The index.html file is what gets sent to the user, and is the main HTML file, which contains the jQuery code.
If you download/fork/test out the code in that repo, open up your browsers developer tools, go to the network tab, and check out the differences.
Using req.params
Using req.query
ORIGINAL ANSWER:
So there are a couple of things wrong with your code and why you are unable to see the value of the textbox server side.
You are sending a PUT request but your server is expecting a GET request
You are looking for the value in req.query when you should be looking for it in req.params
You are looking for the incorrect variable name in your route (on top of using query when you should be using params) req.query.namesearch needs to be req.params.str
See here for more on req.query vs req.params
More detailed examples below.
In your route you are specifying app.get - in other words, you are expecting a GET request to be sent to your server.. but your are sending a PUT request..
If you were sending your AJAX to your server by using something like /teachers?str=someName then you would use req.query.str - or if you wanted to use namesearch you would do: /teachers?namesearch=someName and then to get the value: req.query.namesearch
If you send your AJAX to your server by using the something like /teachers/someName then you should be using req.params.str
// ||
// \/ Server is expecting a GET request
app.get('/teachers/:str', (req, res) => {
// GET THE CORRECT VALUE
let namesearch = req.params.str;
pool.connect((err, client, done) => {
// ... other code here
client.query(
'SELECT * FROM teachers WHERE name = $1',
// SPECIFY THE CORRECT VALUE
namesearch,
(err, result) => {
// ... other code here
})
})
});
But in your AJAX request, you are specifying PUT.. (should be GET)
By default, AJAX will send GET requests, so you really don't have to specify any type here, but I personally like to specify GET in type, just for the sake of brevity - just more succinct in my opinion.
Again, specifying GET in type is not needed since AJAX sends GET by default, specifying GET in type is a matter of preference.
$("#myBtn").click(function () {
// ... other code here
let textboxValue = $("#myTextbox").val();
let theURL = "/teachers/" + textboxValue;
// OR if you wanted to use `req.query.str` server side
// let theURL = "/teachers?str=" + textboxValue;
if (confirm('Search Record?')) {
$.ajax({
url: theURL,
// ||
// \/ You are sending a PUT request, not a GET request
type: 'put', // EITHER CHANGE THIS TO GET OR JUST REMOVE type
// ... other code here
});
}
});
It appears you are grabbing the value correctly from the textbox, you just need to make sure your server is accepting the same type that you are sending.
I am trying to implement a way of only allowing authorized admins to access pages with sensitive information within my Web App/API. I am currently using Node.JS with Express to handle routing. Views are generated with Jade/Pug View Engine. CORS requests are handled using the request framework on github. (https://github.com/request/request)
Currently, clients are stored within a MongoDB collection. When they enter their login information, their passwords are compared to a Hash stored in the DB. If everything checks out, a JWT is generated for the user and saved in the client DB and also stored within the browser in localStorage.token.
The user is then redirected to a splash page which has an authentication function as middleware. The authentication function accepts the token in three forms:
var token = req.body.token || req.query.token || req.headers['x-access-token'];
I feel like the way redirection is handled is a bit hacky. I am using window.location = route?token=[token]
Token is handed over to the authentication function within req.query.token, but this is exposed in URL. Here is my login function:
function submitLogIn() {
var credentials = {
email: logInForm.userEmail.value,
password: logInForm.pwField.value
}
fetch('/login', {
headers:{
'Content-Type': 'application/json'
},
method: 'POST',
body: JSON.stringify(credentials)
}).then(function(res){
if(!res.ok) alert("Error!")
else return res.json().then(function(result){
localStorage.token = result.token;
//This is how I'm doing redirecting:
window.location = '/selection?token=' + result.token;
}).catch(function(err){
console.log(err);
throw err;
});
});
Just for reference, this is the front-end route that it's going to at port 3001 which swings it over to 3000 as a reverse-proxy request:
router.post('/login', function(req, res, next) {
request.post({
url: 'http://localhost:3000/authorize',
form: req.body
}).pipe(res)
});
My main question is this: How can I handle redirection at the front-end within fetch calls?
My splash screen is basically a menu of buttons which will take the user to different pages. The pages will contain information that only admins should see. Let's say I want to click on this navigation button. It goes to a /GET route that requires the token to be sent for an OK status. if no token, it returns a 403.
router.get('/main', authorize.adminRequired, function(req, res, next) {
request.get({
url: 'http://localhost:3000/menus'
}, function(response, sensitiveItems) {
return res.render('index', {menu: JSON.parse(sensitiveItems)});
});
});
That authorize.adminRequired function needs a token in the form of a query, x-access-token, or req.body. Can't send a body in a GET request. Don't want to expose in URL, so:
Here's what I tried. I have this redirect fetch call as an onclick function.
function redirectToSensitivePage() {
fetch('/main', {
headers: {
'x-access-token': localStorage.token
},
method: 'GET'
});
};
This gives me a 200, OK. But I don't load the page. The res.render(index) in the route does not fire. The response does contain all of the data, but I am not redirected to the visual representation.
I could do the same hacky redirect using window.location=/main?token=[token], but I don't want to expose the URL token. I could load everything I need into the webpage upon login instead of having separate pages, have those divs hidden out of sight until keypress to make that div the main visible div, but I would rather figure out how to do it via redirect.
I'm still getting my bearings with using Node, so please let me know if I have made any stupid glaring oversights.
Edit: Should I simply try
var token = localStorage.token || req.body.token || req.query.token || req.headers['x-access-token'];
Is there any disadvantage to simply scraping from the localStorage automatically with each request?
I'm trying to send an authenticated request with one click in postman.
So, I have request named "Oauth" and I'm using Tests to store the token in a local variable.
var jsonData = JSON.parse(responseBody);
postman.setEnvironmentVariable("token", jsonData.access_token);
What I'm trying to do now is that run the Oauth request automatically (from a pre-request script) for any other requests which needs a bearer token.
Is there a way to get an access token and send an authenticated request with one postman button click?
As mentioned by KBusc and inspired from those examples you can achieve your goal by setting a pre-request script like the following:
pm.sendRequest({
url: pm.environment.get("token_url"),
method: 'GET',
header: {
'Authorization': 'Basic xxxxxxxxxx==',
}
}, function (err, res) {
pm.environment.set("access_token", res.json().token);
});
Then you just reference {{access_token}} as any other environment variable.
NOTE: There now is a way to do this in a pre-request script, see the other answers. I'll keep this answer for posterity but just so everyone knows :)
I don't think there's a way to do this in the pre-request script just yet, but you can get it down to just a few clicks if you use a variable and the Tests tab. There are fuller instructions on the Postman blog, but the gist of it is:
Set up your authentication request like normal.
In the Tests section of that request, store the result of that request in a variable, possibly something like the following:
var data = JSON.parse(responseBody);
postman.setEnvironmentVariable("token", data.token);
Run the authentication request -- you should now see that token is set for that environment (click on the eye-shaped icon in the top right).
Set up your data request to use {{token}} wherever you had previously been pasting in the bearer token.
Run your data request -- it should now be properly authenticated.
To refresh the token, all you should need to do is re-run the authentication request.
You can't send another request from Pre-request Script section, but in fact, it's possible to chain request and run one after another.
You collect your request into collection and run it with Collection Runner.
To view request results you can follow other answer.
The same question was on my mind, which is basically "how can I run another request that already exists from another request's test or pre-request script tabs without building that request with pm.sendRequest(reqConfObj)?", then I found the postman.setNextRequest('requestName') method from this Postman discussion which is gonna lead you to this postman documentation page about building request workflows.
But the thing is, postman.setNextRequest() method will not run if you are not running a folder or a collection, so simply hitting the 'Send' button of the request that has your script won't work.
I also would like to draw your attention towards some things:
The prepended word, it's 'postman' instead of 'pm'.
postman.setNextRequest() will always run last, even though you have written it to the top of your script. Your other code in the script will be ran and then postman.setNextRequest will initialize.
If you would like to stop the request flow, you could simply postman.setNextRequest(null).
I would encourage everyone that uses Postman to check out the links that was mentioned, I believe it's a great feature that everybody should give it a try! :)
All these workarounds with recreating requests. Postman does not support what you want to do. In order to get what you want, you have to use Insomnia, it allows you to map body values from other request responses and if those responses are not executed ever it automatically runs them or behaves based on chosen policy.
But if you want to stick with Postman, then you'll have to save full previous request params to global variables, then retrieve all configuration of previous requests from that variable as a JSON string, parse that JSON into an object and assign it to pm.sendRequest as the first argument.
You can add a pre-request script to the collection which will execute prior to each Postman request. For example, I use the following to return an access token from Apigee
const echoPostRequest = {
url: client_credentials_url,
method: 'POST',
header: 'Authorization: Basic *Basic Authentication string*'
};
var getToken = true;
if (!pm.environment.get('token')) {
console.log('Token missing')
} else {
console.log('Token all good');
}
if (getToken === true) {
pm.sendRequest(echoPostRequest, function(err, res) {
console.log(err ? err : res.json());
if (err === null) {
console.log('Saving the token');
console.log(res);
var responseJson = res.json();
console.log(responseJson.access_token);
pm.environment.set('token', responseJson.access_token)
}
});
}
First, add pre-request script:
pm.sendRequest({
url: 'http://YOUR_SITE',
method: 'POST',
body: {
mode: 'urlencoded',
urlencoded: [
{ key: "login", value: "YOUR_LOGIN" },
{ key: "password", value: "YOUR_PASSWORD" }
]
}
}, function (err, res) {
if (err === null) {
pm.globals.set("token", res.json()["access_token"]);
}
});
Second, set custom variable(after you can use it value):
Third, you can use variable by {{VARIABLENAME}}, for example:
If you are setting token in your auth token you can copy its request configuration to env once (in test script section) and automatically call it from other request and use token from env.
It originally mentuioned here: https://community.postman.com/t/use-existing-postman-request-in-pre-post-script/23477/5
Copy current request config to env in auth test script:
let r=pm.response.json();
pm.environment.set("access_token", r.access_token);
pm.environment.set("refresh_token", r.refresh_token);
pm.environment.set("auth_req", pm.request);
And then call it on other endpoints:
pm.sendRequest(pm.environment.get("auth_req"))
I have tried multiple solutions, the below solution is related to when you are parsing the response for request 1 and passing any variable into the second request parameter. ( In this Example variable is Lastname. )
Note:- data and user are JSON objects.``
postman.clearGlobalVariable("variable_key");
postman.clearEnvironmentVariable("variable_key");
tests["Body matches string"] = responseBody.has("enter the match string ");
var jsonData = JSON.parse(responseBody);
var result = jsonData.data;
var lastName = result.user.lastName;
tests["Body matches lastName "] = responseBody.has(lastName);
tests["print matches lastName " + lastName ] = lastName;
You can use a function sendRequest of postman.
Some examples here.
https://gist.github.com/sid405/b57985b0649d3407a7aa9de1bd327990
I have my test.login.js:
it('calls login when there\'s a username present', () => {
React.findDOMNode(LoginElement.refs.username).value = 'foo';
TestUtils.Simulate.submit(form);
expect(LoginElement.state.errored).toEqual(false);
});
By submitting the form, it calls a login method:
login() {
let typedUsername = React.findDOMNode(this.refs.username).value;
if (!typedUsername) {
return this.setState({
errored: true
});
}
// we don't actually send the request from here, but set the username on the AuthModel and call the `login` method below
AuthModel.set('username', typedUsername);
AuthModel.login();
},
So I'm trying to test the functionality of Login.jsx, not AuthModel.js, however by calling AuthModel.login(), it sends a message over a WebSocket. However, the issue is that in my actual app, I don't load anything until the WebSocket has connected (I fire an event to then render the React app), however in my Jasmine test, I don't wait for this event, so I receive:
ERROR: null, DOMException{stack: 'Error: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.
And my test fails, which, it shouldn't fail because it's encapsulated functionality does what I want it to. It just errors further up the dependency tree.
What is my best approach for either working around this, or to mitigate the WebSocket trying to connect in my test env? (I'm extremely new to testing, so these concepts are very alien to me right now)
I won't pretend to know a lot about this, but can't you dependency inject AuthModel so how and then mock it in your tests? Sorry this isn't a complete answer it's just what my first instinct would be.
If you need a library to assist this, angular/di (from angular2) is pretty great.
You could mock / stub the server request using Sinon JS. - http://sinonjs.org/
If you just want to know that Auth.login makes a request to the server, use sinon.stub (http://sinonjs.org/docs/#stubs), e.g.
var stub = sinon.stub($, 'ajax');
//Do something that calls $.ajax
//Check stub was called and arguments of first call:
console.log(stub.called)
console.log(stub.args[0])
stub.restore();
If your code requires a response, use sinon's fake server (http://sinonjs.org/docs/#server):
var server = sinon.fakeServer.create(),
myResults = [1, 2, 3];
//Set-up server response with correct selected tags
server.respondWith('POST', url, [200, {
'Content-Type': 'application/json'
},
JSON.stringify({
response: myResults
})
]);
//Do something which posts to the server...
sendToServer('abc').done(function(results) {
console.log('checking for results ', results);
})
server.restore();
You can get a lot more complicated with the server responses - using functions, etc. to handle multiple request types, e.g.
function initServer(respondOk) {
var server = sinon.fakeServer.create();
server.respondWith('POST', /.*\/endpoint\/.*/, function(request) {
var header = { 'Content-Type': 'application/json' };
if(!respondOk) {
var response = JSON.stringify([{
'error_code': '500',
'message': 'Internal server error'
}]);
request.respond(500, header, response);
} else {
var code = 200,
resources = JSON.parse(request.requestBody),
result = JSON.stringify({ customer: resources });
request.respond(code, header, result);
}
});
return server;
});