I am using the phantomjs-node api to render a PDF. The problem is that words that appear near the end of a page are split across 2 pages, half the word on page 1, other half on page 2. I cannot understand why this is happening. What can I do to fix this? I already created an issue on phantomjs github with no response yet.
The length of data is unknown until runtime so I don't see it being feasible to put a hard page break in the rows of data.
Which version of PhantomJS are you using?
phantomjs-node ^v2.1.21
What steps will reproduce the problem?
a. Create multi page website
b. Render as pdf
Which operating system are you using?
windows 10 x64
Did you use binary PhantomJS or did you compile it from source?
phantomjs-node pre-compiled
Please provide any additional information below.
As of 2018-05-03 (Mar. 3) the development of PhantomJS is suspended due to lack of active contribution and it is archived. Link
Because of this update, I recommend Google's official headless wrapper around Chrome, puppeteer.
Install:
npm i puppeteer
Usage:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({path: 'example.png'});
await browser.close();
})();
Note
This answer won't provide a helpful answer to your question, but sadly, you'll face the fact, that you have to leave PhantomJS and switch to another headless solution, sooner or later... Our team have faced with this issue, we chose puppeteer.
Related
I have just started to use CodeceptJS and I'm having some trouble getting the seeVisualDiffForElement command working.
This is my test code so far:
Feature('MyTest');
Scenario('Creating base images and comparing using the same command but ran twice', (I) => {
I.amOnPage('https://testsite-90d35.firebaseapp.com/');
I.wait(1);
I.saveScreenshot("Test1.png");
I.wait(1);
I.seeVisualDiff("Test1.png", {tolerance: 0, prepareBaseImage: true});
});
Scenario ('Comparing the blue button on the page', async (I) => {
I.amOnPage('https://testsite-90d35.firebaseapp.com/testPage.html');
I.wait(1);
I.saveScreenshot("Test1.png");
I.wait(1);
I.seeVisualDiffForElement(".btnBlue","Test1.png", {tolerance: 0, prepareBaseImage: false});
});
When I run this code it opens the Chromium browser (as I'm using it with Puppeteer) and runs through the first scenario just fine. Takes the screenshot and saves it as a base image. Then I have to close the browser for it to run the next scenario. After the 2nd scenario has ran it fails the test but still creates the screenshots. The screenshots are different as they should be but the diff screenshot doesn't have any outlined changes.
In the documentation for CodeceptJS it states "seeVisualDiffForElement only works when the page for baseImage is open in the browser, so that webdriver can fetch coordinates of the provided selector". My browser is open but not the same browser that created the base image (as I have to close it to run the 2nd scenario). Could this be why the test fails and doesn't recognise any changes? Or can anyone see something else I'm doing wrong?
I've tried to run the tests as a single scenario as well but it gives me the same output.
I was informed that element testing only works with WebDriver, but I was using Puppeteer, hence the errors.
We have a website that has many paths of flow (login , signup , payment ,etc)
We're using puppeteer scripts ( typescript via node) to automate-testing our website behaviour (full flow) , and when we get an error (or unexpected result) we're sending email or some kind of alerts.
But I see that people also use jasmine with puppeteer.
For example :
const puppeteer = require('puppeteer');
describe("Jasmine puppeteer", function() {
let browser;
let page;
beforeAll(() => {
browser = await puppeteer.launch({headless: false});
page = await browser.newPage();
await page.goto('chrome://newtab');
await page.screenshot({path: 'a.png'});
})
it("jasmine puppeteer", () => {
expect(await page.title()).toBe("");
done();
});
afterAll(() => {
})
});
Using a testing framework over automated testing framework seems (to me) like Test(Test())
Question
Should we change our site approach testing to jasmin over puppeteer ? I mean , currently puepetteer provides a good way to test our site flow. Should we need to apply jasmine testing over our existing tests scripts ? I'm a bit confused about that.
You can use jest with puppeteer for end to end testing. Jest is based on Jasmine framework. It is developed by Facebook and itβs quite popular now.
puppeteer is not a testing framework.
puppeteer is a tool that automate browser.
you cannot make any assert with puppeteer, so you need a testing framework.
a good choise for puppeteer is jest,
because jest come out of the box with everything you need.
you can also use mocha and chai,
but i suggest jest because you can start to use immediately.
While trying to set up some simple end-to-end tests with Jest and Puppeteer, I've found that any test I write will inexplicably fail with a timeout.
Here's a simple example test file, which deviates only slightly from Puppeteer's own example:
import puppeteer from 'puppeteer';
describe('Load Google Puppeteer Test', () => {
test('Load Google', async () => {
const browser = await puppeteer.launch({
headless: false
});
const page = await browser.newPage();
await page.goto('https://google.co.uk');
await expect(page).toMatch("I'm Feeling Lucky");
await browser.close();
});
});
And the response it produces:
TimeoutError: Text not found "I'm Feeling Lucky"
waiting for function failed: timeout 500ms exceeded
I have tried adding in custom timeouts to the goto line, the test clause, amongst other things, all with no effect. Any ideas on what might be causing this? Thanks.
What I would say is happening here is that using toMatch expects text to be displayed. However, in your case, the text you want to verify is text associated with a button.
You should try something like this:
await expect(page).toMatchElement('input[value="I\'m Feeling Lucky"]');
Update 1:
Another possibility (and it's one you've raised yourself) is that the verification is timing out before the page has a chance to load. This is a common issue, from my experience, with executing code in headless mode. It's very fast. Sometimes too fast. Statements can be executed before everything in the UI is ready.
In this case you're better off adding some waitForSelector statements throughout your code as follows:
await page.waitForSelector('input[value="I\'m Feeling Lucky"]');
This will ensure that the selector you want is displayed before carrying on with the next step in your code. By doing this you will make your scripts much more robust while maintaining efficiency - these waits won't slow down your code. They'll simply pause until puppeteer registers the selector you want to interact with / verify as being displayed. Most of the time you won't even notice the pause as it will be so short (I'm talking milliseconds).
But this will make your scripts rock solid while also ensuring that things won't break if the web page is slower to respond for any reason during test execution.
You're probably using 'expect-puppeteer' package which does the toMatch expect. This is not a small deviation. The weird thing is that your default timeout isn't 30 seconds as the package's default, check that.
However, to fix your issue:
await expect(page).toMatch("I'm Feeling Lucky", { timeout: 6000 });
Or set the default timeout explicitly using:
page.setDefaultTimeout(timeout)
See here.
Every time you deploy to Firebase hosting a new deploy version is created so you can roll back and see who deployed. This means that each time every file you deploy is stored and occupying more space.
Other than manually deleting each deployed version one by one, is there any automated way to clean those useless files?
You're correct. You'll need to delete the old deployed versions one by one using the Firebase Hosting console.
There's no other way to do this, so I'd suggest you to file a feature request to enable deletion of multiple deployed version in the Firebase Hosting console.
Update:
You can vote here (please avoid +1 spam, use reactions) https://github.com/firebase/firebase-tools/issues/215#issuecomment-314211730 for one of the alternatives proposed by the team (batch delete, keep only X versions, keep versions with published date < Y)
UPDATE Mar/2019
There's now a proper solution: "Version history settings" which allows to keep the last X versions.
https://support.google.com/firebase/answer/9242086?hl=en
UPDATE Feb/2019
Confirmed by Google employee # github.com/firebase/firebase-tools/issues/...
It is actively being worked on. π
πππ
Before continuing reading:
You can vote here (please avoid +1 spamming, use reactions) https://github.com/firebase/firebase-tools/issues/215#issuecomment-314211730 for one of the alternatives proposed by the team
So, by using Chrome Dev tools I found a way to delete multiple versions. Keep in mind it requires a bit for work (proceed with care since deleted versions can't be restored and you won't get any warnings like when using the UI).
Step 1. Retrieving the version list.
Open Chrome Dev Tools (if you don't know how to chances are you should wait for a proper solution by Firebase's team).
Open Firebase's Console and go to the "Hosting" tab.
Go to the "Network" tab on CDT and use the Websockets filter.
Select the request named .ws?v=5&ns=firebase
Open the "Frames" tab
Now comes the tedious part: Select the frames with the highest "length" value. (Depending on your data, it could be 2-n frames. In my case, 3 frames with 14k-16k length)
Paste each of the frame's data in order (which will form a valid JSON object).
Extracting the data: There are several ways to do it. I opted for simple JS on CDT's console.
var jsonString = '...';
var json = JSON.parse(jsonString);
var ids = Object.keys(json.d.b.d);
Step 2. Performing the requests
Almost there :P
Now that you have the IDs, perform the following requests:
DELETE https://firebasehosting.clients6.google.com/v1beta1/sites/PROJECT_NAME/versions/-VERSION_ID?key=KEY
I used Sublime (to create the request strings) + Paw.
The "KEY" can be copied from any of CDT's requests. It doesn't match Firebase's Web API key
=> Before performing the requests: take note of the version you don't want to delete from the table provided by Firebase. (Each version listed on the website has the last 6 digits of it's ID under your email)
(Screenshots weren't provided since all of them would require blurring and a bit of work)
This script is not yet super-solid, so use it at your own risk. I'll try to update it later, but worked for me for now.
Just some javascript to click on buttons to delete deployed items one by one.
var deleteDeployment = function(it) {
it.click()
setTimeout(function() {
$('.md-dialog-container .delete-dialog button.md-raised:contains("Delete")').click()
}, 300)
}
$('.h5g-hist-status-deployed').map((i, a) => $(a).parent()).map((i, a) => $(a).find('md-menu button:contains(Delete)')).each((i, it) => {
setTimeout(function() {
deleteDeployment(it)
}, (i + 1) * 2000)
})
Firebase finally implemented a solution for this.
It is now possible to set a limit of retained versions.
https://firebase.google.com/docs/hosting/deploying#set_limit_for_retained_versions
EDIT: previous link is outdated. Here is a new link that works:
https://firebase.google.com/docs/hosting/usage-quotas-pricing#control-storage-usage
This may be a bit brittle due to the selectors' reliance on current DOM structure and classes on the Hosting Dashboard, but it works for me!
NOTE: This script (if executed from the console) or bookmarklet will click and confirm delete on all of the rows in the current view. I'm fairly certain that even if you click delete on the current deployment it will not delete it.
Function for running in console:
let deleteAllHistory = () => {
let deleteBtns = document.querySelectorAll('.table-row-actions button.md-icon-button');
const deleteBtn = (pointer) => {
deleteBtns[pointer].click();
setTimeout(() => {
document.querySelector('.md-open-menu-container.md-clickable md-menu-item:last-child button').click();
setTimeout(() => {
document.querySelector('.fb-dialog-actions .md-raised').click();
if(pointer < deleteBtns.length - 1) {
deleteBtn(pointer + 1);
}
}, 500);
}, 500);
};
deleteBtn(0);
};
Bookmarklet:
javascript:(function()%7Bvar%20deleteBtns%3Ddocument.querySelectorAll('.table-row-actions%20button.md-icon-button')%2CdeleteBtn%3Dfunction(a)%7BdeleteBtns%5Ba%5D.click()%2CsetTimeout(function()%7Bdocument.querySelector('.md-open-menu-container.md-clickable%20md-menu-item%3Alast-child%20button').click()%2CsetTimeout(function()%7Bdocument.querySelector('.fb-dialog-actions%20.md-raised').click()%2Ca%3CdeleteBtns.length-1%26%26deleteBtn(a%2B1)%7D%2C500)%7D%2C500)%7D%3BdeleteBtn(0)%7D)()
Nathan's option is great, but I have a quick-and-dirty method using AutoHotkey. Takes about a second per version to delete, so you can knock out a page in 10 seconds.
#a::
Click
MouseGetPos, xpos, ypos
MouseMove, xpos, ypos + 30
Sleep 300
Click
Sleep 400
Click 1456, 816
MouseMove, xpos, ypos + 82
return
#s::
Click
MouseGetPos, xpos, ypos
MouseMove, xpos, ypos - 820
return
You'll likely need to modify the exact pixel values for your screen, but this works perfectly on my 1920x1080.
Win + a is delete and move to the next entry, Win + s is move to the next page. Put your mouse on the first 3-dot menu and go for it!
On top of the release history table, click the tool bar and select "Version history settings". Set to desired amount and click save.This will auto delete older deployments.
I don't know it can help you or not but I can delete old deployments from "hosting" menu like this:
Delete or rollback old deployment
I have used the code below:
Iterable<Module> modules = ImmutableSet.<Module> of(
new SshjSshClientModule());
ContextBuilder builder = ContextBuilder.newBuilder(provider).endpoint(endpoint)
.credentials(identity, credential)
.modules(modules);
System.out.printf(">> initializing %s%n", builder.getApiMetadata());
ComputeService compute = builder.buildView(ComputeServiceContext.class).getComputeService();
System.out.println(compute1.listImages());
but I am getting the following error message.........
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_ARRAY at line 1 column 787
at org.jclouds.json.internal.DeserializationConstructorAndReflectiveTypeAdapterFactory$DeserializeIntoParameterizedConstructor.read(DeserializationConstructorAndReflectiveTypeAdapterFactory.java:181)
at org.jclouds.json.internal.NullFilteringTypeAdapterFactories$IterableTypeAdapter.readAndBuild(NullFilteringTypeAdapterFactories.java:92)
The code was working... before...
You've definitely hit a bug somewhere between the version of jclouds you're using and whatever version of whatever cloud you're using. We'll need more information to fix this. Please go through the instruction on how to Report a Bug to Apache jclouds.