Cypress e2e testing: How to access new tab by clicking on "href". I don't have target attribute to remove and test on new opened tab? - tabs

This attached image is my HTML code for href which will open a new tab
The DOM has iframe so I wrote below code accessed the href and it will open in new tab. I am unable to access the newly opened tab, though I know the method that would have target attribute so we remove that and open in same tab but here I don't have any target attributes.
Please check this and help to access my new tab.
cy.visit('https://yopmail.com/en/')
cy.get('.ycptinput').type('some_name {enter}')
cy.wait(2000)
cy.get('#ifmail').its('0.contentDocument.body').then(cy.wrap).find('a').click()

The cy.origin() command is meant to solve the "new tab" problem.
It's a bit new, so expect some teething problems. Basically, it sets up a sand-boxed domain that can use Cypress commands.
Anything from outside cy.origin() that you want to use inside (for example, the link you found) needs special handling to pass in.
It gets passed in on a special args option, and is received in the same pattern.
let link;
cy.visit('https://yopmail.com/en/')
cy.get('.ycptinput').type('some_name {enter}')
cy.wait(2000)
cy.get('#ifmail').its('0.contentDocument.body')
.then($body => {
link = $body.find('a')[0].href
})
cy.then(() => { // this just waits for above block to complete
const newOrigin = link.split('?')[0] // remove query params
.replace('http://', 'https://') // correct for secure protocol
cy.origin(newOrigin, { args: { link } }, ({ link }) => {
cy.visit(link) // same as ".find('a').click()" but works cross-domain
})
})

Related

can make extinction chrome work automatically when open chrome [duplicate]

I'm writing a Chrome extension and trying to overlay a <div> over the current webpage as soon as a button is clicked in the popup.html file.
When I access the document.body.insertBefore method from within popup.html it overlays the <div> on the popup, rather than the current webpage.
Do I have to use messaging between background.html and popup.html in order to access the web page's DOM? I would like to do everything in popup.html, and to use jQuery too, if possible.
ManifestV3 service worker doesn't have any DOM/document/window.
ManifestV3/V2 extension pages (and the scripts inside) have their own DOM, document, window, and a chrome-extension:// URL (use devtools for that part of the extension to inspect it).
You need a content script to access DOM of web pages and interact with a tab's contents. Content scripts will execute in the tab as a part of that page, not as a part of the extension, so don't load your content script(s) in the extension page, use the following methods:
Method 1. Declarative
manifest.json:
"content_scripts": [{
"matches": ["*://*.example.com/*"],
"js": ["contentScript.js"]
}],
It will run once when the page loads. After that happens, use messaging but note, it can't send DOM elements, Map, Set, ArrayBuffer, classes, functions, and so on - it can only send JSON-compatible simple objects and types so you'll need to manually extract the required data and pass it as a simple array or object.
Method 2. Programmatic
ManifestV2:
Use chrome.tabs.executeScript in the extension script (like the popup or background) to inject a content script into a tab on demand.
The callback of this method receives results of the last expression in the content script so it can be used to extract data which must be JSON-compatible, see method 1 note above.
Required permissions in manifest.json:
Best case: "activeTab", suitable for a response to a user action (usually a click on the extension icon in the toolbar). Doesn't show a permission warning when installing the extension.
Usual: "*://*.example.com/" plus any other sites you want.
Worst case: "<all_urls>" or "*://*/", "http://*/", "https://*/" - when submitting into Chrome Web Store all of these put your extension in a super slow review queue because of broad host permissions.
ManifestV3 differences to the above:
Use chrome.scripting.executeScript.
Required permissions in manifest.json:
"scripting" - mandatory
"activeTab" - ideal scenario, see notes for ManifestV2 above.
If ideal scenario is impossible add the allowed sites to host_permissions in manifest.json.
Some examples of the extension popup script that use programmatic injection to add that div.
ManifestV3
Don't forget to add the permissions in manifest.json, see the other answer for more info.
Simple call:
(async () => {
const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
await chrome.scripting.executeScript({
target: {tabId: tab.id},
func: inContent1,
});
})();
// executeScript runs this code inside the tab
function inContent1() {
const el = document.createElement('div');
el.style.cssText = 'position:fixed; top:0; left:0; right:0; background:red';
el.textContent = 'DIV';
document.body.appendChild(el);
}
Note: in Chrome 91 or older func: should be function:.
Calling with parameters and receiving a result
Requires Chrome 92 as it implemented args.
Example 1:
res = await chrome.scripting.executeScript({
target: {tabId: tab.id},
func: (a, b) => { return [window[a], window[b]]; },
args: ['foo', 'bar'],
});
Example 2:
(async () => {
const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
let res;
try {
res = await chrome.scripting.executeScript({
target: {tabId: tab.id},
func: inContent2,
args: [{ foo: 'bar' }], // arguments must be JSON-serializable
});
} catch (e) {
console.warn(e.message || e);
return;
}
// res[0] contains results for the main page of the tab
document.body.textContent = JSON.stringify(res[0].result);
})();
// executeScript runs this code inside the tab
function inContent2(params) {
const el = document.createElement('div');
el.style.cssText = 'position:fixed; top:0; left:0; right:0; background:red';
el.textContent = params.foo;
document.body.appendChild(el);
return {
success: true,
html: document.body.innerHTML,
};
}
ManifestV2
Simple call:
// uses inContent1 from ManifestV3 example above
chrome.tabs.executeScript({ code: `(${ inContent1 })()` });
Calling with parameters and receiving a result:
// uses inContent2 from ManifestV3 example above
chrome.tabs.executeScript({
code: `(${ inContent2 })(${ JSON.stringify({ foo: 'bar' }) })`
}, ([result] = []) => {
if (!chrome.runtime.lastError) {
console.log(result); // shown in devtools of the popup window
}
});
This example uses automatic conversion of inContent function's code to string, the benefit here is that IDE can apply syntax highlight and linting. The obvious drawback is that the browser wastes time to parse the code, but usually it's less than 1 millisecond thus negligible.

When does browser read the file selected in input type=file

My HTML page has a <input type="file"/> element, and I do the following steps:
Click browse and select a file
Edit the file contents from disk
Click on form submit
What are the contents that are expected to go to the server in such case? Is there a definition in any spec as to what should happen (i.e the original contents should be sent, or the new contents should be sent) or is the implementation left to the browser?
This depends on how the file input is used. However, in the most general scenario, the file content is read at the upload time.
When you execute the submit action of a form, it will go through all its elements and compose an HTTP request with all the input data. It is at this specific time the physical file on the disk is read by the form action.
Now, there are other manipulations of form submission commonly done in web applications. The file content, for example, can be read immediately by the onChange event of the file input element and an application can store this data in a hidden element inside the form. It may be this data embedded in the hidden element that the server is really considering.
Your non-modified data will be submitted to the server in this scenario.
I just created a simple example and found that browser keep the reference to the uploaded file.
In the nutshell:
Create foo.txt file
Add 'Hello world' content
Save file
Upload the file by using <input type="file />
browser keeps reference to the selected file
Make some changes in the file. Change 'Hello world' to 'Hello world 2'
Press submit button
The file foo.txt with 'Hello world 2' will be submitted.
You can try to test this scenario by using snippet below. Upload the file and make some changes during setTimeout().
const input = document.querySelector('#input');
let cache = null;
function onFileChange(event) {
const [ file ] = event.target.files;
cache = file;
readFileData(file).then(result => {
console.log('Result:', result);
// Edit file during the timeout
setTimeout(() => {
console.log('File', cache);
}, 10000)
})
}
function readFileData(file) {
const reader = new FileReader(file);
return new Promise((resolve, reject) => {
reader.onload = event => resolve(event.target.result);
reader.onerror = error => reject(error)
reader.readAsText(file);
});
}
input.addEventListener('change', onFileChange);
<form>
<input id="input" type="file" />
</form>
Update:
Also it is possible to read data into the local variable after file has been uploaded. In such case, you will keep local copy of entire file and it will not be affected.

Making Chrome extension make an existing tab active and canceling the incoming request with chrome.webRequest.onBeforeRequest listener

I'm building a search app and when a user tries to navigate to the URL of a search result, I want to:
A) open a new tab with that URL if they don't already have that URL open in an existing tab, or
B) if they already have a tab with that URL open, make that existing tab active and refrain from opening another new tab with that same URL.
Separately from the code below, I have a currentTabs dict that's keeping track of the tabId and URL of each open tab so that I can compare the incoming URL to the already open URLs.
I'm trying to add a listener on
chrome.webRequest.onBeforeRequest
to accomplish this. I'm using a query string like ?from_my_app=true so that I only implement this behavior on links from my app rather than any time someone tries to navigate to a URL they already have open. My code looks like this:
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
if (details.url.match(/?from_my_app=true/)) {
var strippedUrl = (details.url.split('?from_my_app=true')[0];
var matchedUrl;
if (currentTabs.tabs) {
// get the first pre-existing tab that has this url
matchedUrl = currentTabs.tabs.filter(x => x[1] == strippedUrl)[0];
}
if (matchedUrl) {
// make the pre-existing tab with this url active
// and cancel the opening of a new tab opening this url
chrome.tabs.update(matchedUrl[0], { active: true });
return { cancel: true }
} else {
// if this URL isn't already open in a tab, remove the
// ?from_my_app=true query string and open it in a new
// tab
return { redirectUrl: strippedUrl };
}
}
},
{ urls: ["<all_urls>"] },
["blocking"]
);
The issue I'm running into is that the
return { cancel: true }
executed when a URL is already open in another tab is causing the tab to not load at all and say " is blocked.
Requests to the server have been blocked by an extension." Clearly I'm using the API wrong - the behavior I'm looking for is for it to basically make the incoming request "fail silently" and just do the navigating to the existing tab while not opening a new tab at all. Is there a clean way to do this?

Opening a PDF Blob in a new Chrome tab (Angular 2)

I am loading a PDF as follows (I am using Angular 2, but I am not sure that this matters..):
//Inside a service class
downloadPdf = (id): Observable<Blob> => {
let headers = new Headers();
headers.append("Accept", "application/pdf");
return this.AuthHttp.get(this.pdfURL + id, {
headers: headers,
responseType: ResponseContentType.Blob
}).map(res => new Blob([res.blob()], {type: "application/pdf"}));
}
//Inside a click handler
this.pdfService.downloadPdf(this.id).subscribe((data: Blob) => {
let fileURL = window.URL.createObjectURL(data);
window.open(fileURL);
});
This code runs nicely in Firefox. In Chrome, a new tab briefly flashes open and closes. When I debug and I manually put surf to the object URL, Chrome can open it just fine.
What am I doing wrong here?
The opening of a new tab got blocked by an adblocker.
It can not work, new popup will be blocked by browser, because of it was created from callback which is not a trusted event, to make it work it must be called directly from click handler, or you have to disable bloking popups in your browser.
Chrome will only allow this to work as wanted if the ajax call returns in less than a second. More there

passing a value from background.js to popup

In background.js, I create a popup like so:
chrome.windows.create({
focused: true,
width: 1170,
url : "settings/index.html",
type: "popup"
}, function(popup) {
tab_app = popup.id;
alert(tab_app);
});
I store the id in tab_app.
how can I pass a value from background.js to my popup?
I'm trying like that:
chrome.tabs.executeScript(tab_app, {code: "alert("+message.add+");"});
but it keeps telling me that this tab id doesnt exist.. im assuming its because its a popup. will appreciate some help.
Since it's your extension page, the method of choice is Messaging.
Note: you can't use the per-tab messaging of chrome.tabs.sendMessage, since this explicitly targets the content script context (that doesn't exist for extension pages). You need to use the "broadcast" chrome.runtime.sendMessage that will send to all other extension pages.
If you can have more than one popup-type window at a time, this may be a problem - you need some identifier to go along. You could pass it as a URL parameter or a URL hash, e.g. "settings/index.html?id=foo" or "settings/index.html#foo". If you don't expect more than one popup-type window (you can always check if one is open before opening a new one), it doesn't matter.
If you really need dynamic code loading or execution, not just passing data (doubtful), you need to be mindful of CSP.
You can dynamically load a script from your extension's package by just creating and adding a <script> tag to the document.
However, you can't, by default, pass a string of code and eval it in the extension context. You could add 'unsafe-eval' to CSP string, but that's a bad idea in general.
Most probably, you only need some commands to be passed along with data. Pure messaging is great for it, just look at the docs.
This old answer of mine may be of use - I'm using opening a new tab and passing data there to print it.
You cannot call executeScript in the your extension pages. If you try to use executeScript in your extension page. It will show error :
Unchecked runtime.lastError while running tabs.executeScript: Cannot
access contents of url
"chrome-extension://extension_id/yourPage.html".
Extension manifest must request permission to access this host
Now you cannot add "chrome-extension://<extension_id>/yourPage.html" under permissions in manifest.json because it is invalid and not allowed.
Instead you can use message passing.
background.js:
function createNewtab(){
var targetId = null;
chrome.tabs.onUpdated.addListener(function listener(tabId, changedProps) {
if (tabId != targetId || changedProps.status != "complete")
return;
chrome.tabs.onUpdated.removeListener(listener);
chrome.tabs.sendMessage(targetId, {message : "loadNewTab"},function(response){
// do nothing yet
});
});
chrome.windows.create({
focused: true,
width: 1170,
url : chrome.extension.getURL("settings/index.html"),
type: "popup"
}, function(popup) {
targetId = popup.tabs[0].id;
});
}
index.js:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){
switch (request.message){
case "loadNewTab":
alert("HI")
break;
}
});