Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for Android devices #79

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ yarn.lock
# project
.tmp
lib

# Mac OSX
.DS_Store
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OS dependent stuff should be ignored in a global .gitignore file, but I'm fine with merging this in to avoid this in the future.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean by a global .gitignore, I presumed that was this.

2 changes: 1 addition & 1 deletion src/utils/ScreenDimension.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export default class ScreenDimensions {
}

getScale() {
if (this.isIOS) {
if (this.isIOS || this.isAndroid) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a test case for this change?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me see if I can add some off of the existing ones

return this.getScreenWidth() / this.getViewportWidth();
}
return 1;
Expand Down
4 changes: 2 additions & 2 deletions src/utils/ScreenshotStrategyManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ export default class ScreenshotStrategyManager {
return new FullpageScreenshotStrategy(screenDimensions);
}

const { isIOS } = browser;
if (isIOS) {
const { isMobile } = browser;
if (isMobile) {
Copy link
Owner

@zinserjan zinserjan Aug 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really necessary for android simulators or in other words, is merging enough? iOS uses this to remove the grey line between the stiched screenshots.

For real android devices with chrome this shouldn't be executed, merging ist enough.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, let me check just using the merge strategy on Android.

log('use iOS Trim and Merge viewport strategy')
return new TrimAndMergeViewportStrategy(screenDimensions);
}
Expand Down
10 changes: 10 additions & 0 deletions src/utils/mobile/fixedHeights.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete this file. Have a look at my other comment for the reason.

android: {
toolbarHeight: 43,
addressbarHeight: 77
},
iOS: {
toolbarHeight: 44,
addressbarHeight: 44
}
};
26 changes: 15 additions & 11 deletions src/utils/normalizeScreenshot.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import CropDimension from './CropDimension';
import getBase64ImageSize from './getBase64ImageSize';
import { cropImage, scaleImage } from './image';
import fixedHeights from './mobile/fixedHeights';

async function normalizeRetinaScreenshot(browser, screenDimensions, base64Screenshot) {
// check if image dimensions are different to viewport as browsers like firefox scales images automatically down
Expand All @@ -18,16 +19,19 @@ async function normalizeRetinaScreenshot(browser, screenDimensions, base64Screen
return base64Screenshot;
}

async function normalizeIOSScreenshot(browser, screenDimensions, base64Screenshot) {
const toolbarHeight = 44; // bottom toolbar has always a fixed height of 44px
const addressbarHeight = 44; // bottom toolbar has always a fixed height of 44px
async function normalizeMobileScreenshot(browser, screenDimensions, base64Screenshot) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please create a seperate normalize function for Android. Using a normalizer that does iOS and android at the same time is just complicated and uncecessary.
I made this mistake around two years ago within an unpublished webdrivercss fork with support for iOS and Android. In the beginning it looks like a good idea but it becomes unmaintainable very quickly...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

const mobileFixedHeights = (browser.isAndroid) ? fixedHeights.android : fixedHeights.iOS;
const toolbarHeight = mobileFixedHeights.toolbarHeight;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Android really have a toolbar at the bottom? I can't see any at your screenshots.
When not please remove this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to update the test case screenshots.
I don't know why it appears in some cases either, but when I run locally 5.1 it doesn't appear. SauceLabs emulator for 5.1 android however, shows the following. Note the footer.
android_footer

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uiii, this doesn't look like a case that is fixable. Especially with the offsets around the device screenshot and the android controls on the right side 😁

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this is a screenshot from the saucelabs video 😅
The device screenshot is just the header, page, and footer. But that will need cropping

const addressbarHeight = mobileFixedHeights.addressbarHeight;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the height of the adressbar is something that we can calculate with screenHeight - innerHeight = 77.

In my opinion this makes all static height values unnecessary, am I wrong?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that toolbar appears only in some cases. Is there a way to get the y offset of the viewport from the top of the screen? If so then I can calculate whether the footer appears. I think if I use screenHeight - innerHeight and the footer is there, it will be the height of the address bar + the toolbar.


const viewportHeight = screenDimensions.applyScaleFactor(screenDimensions.getViewportHeight());
const viewportWidth = screenDimensions.applyScaleFactor(screenDimensions.getViewportWidth());
let isIpad = false;

// all iPad's have 1024..
const isIpad = screenDimensions.getScreenHeight() === 1024 || screenDimensions.getScreenWidth() === 1024;
const isIphone = !isIpad;
if (browser.isIOS) {
isIpad = screenDimensions.getScreenHeight() === 1024 || screenDimensions.getScreenWidth() === 1024;
}

// detect if status bar + navigation bar is shown
const barsShown = viewportHeight < screenDimensions.getScreenHeight();
Expand All @@ -37,7 +41,7 @@ async function normalizeIOSScreenshot(browser, screenDimensions, base64Screensho
// calculate height of status + addressbar
barsHeight = screenDimensions.getScreenHeight() - viewportHeight;

if (isIphone && barsHeight > addressbarHeight) {
if (!isIpad && barsHeight > addressbarHeight) {
// iPhone's have also sometimes toolbar at the bottom when navigation bar is shown, need to consider that
barsHeight -= toolbarHeight;
}
Expand All @@ -58,18 +62,18 @@ async function normalizeIOSScreenshot(browser, screenDimensions, base64Screensho
return base64Screenshot;
}


export default async function normalizeSreenshot(browser, screenDimensions, base64Screenshot) {
export default async function normalizeScreenshot(browser, screenDimensions, base64Screenshot) {
let normalizedScreenshot = base64Screenshot;

// check if we could have a retina image
if (screenDimensions.getPixelRatio() > 1) {
normalizedScreenshot = await normalizeRetinaScreenshot(browser, screenDimensions, normalizedScreenshot);
}

// check if we have to crop navigation- & toolbar for iOS
if (browser.isMobile && browser.isIOS) {
normalizedScreenshot = await normalizeIOSScreenshot(browser, screenDimensions, normalizedScreenshot);
// check if we have to crop navigation- & toolbar for iOS or Android
if (browser.isMobile) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As already said, please separate iOS from android here for maintenance reasons.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to make sure that you doesn't break real android devices with chrome here. They don't need additional cropping as the screenshots are the same like the desktop chrome.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, does the viewportHeight and screenHeight return different values for real device chrome? If not then it should just pass through without cropping. If so then maybe there is a way to check for emulators.

normalizedScreenshot = await normalizeMobileScreenshot(browser, screenDimensions, normalizedScreenshot);
}

return normalizedScreenshot;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"body": {
"offsetHeight": 0,
"scrollHeight": 480
},
"window": {
"pixelRatio": 2,
"orientation": 0,
"innerWidth": 320,
"screenHeight": 480,
"innerHeight": 403,
"screenWidth": 320
},
"html": {
"clientWidth": 320,
"offsetHeight": 0,
"scrollWidth": 320,
"clientHeight": 480,
"scrollHeight": 0
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"body": {
"offsetHeight": 0,
"scrollHeight": 480
},
"window": {
"pixelRatio": 2,
"orientation": 0,
"innerWidth": 320,
"screenHeight": 480,
"innerHeight": 403,
"screenWidth": 320
},
"html": {
"clientWidth": 320,
"offsetHeight": 0,
"scrollWidth": 320,
"clientHeight": 480,
"scrollHeight": 0
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"body": {
"offsetHeight": 0,
"scrollHeight": 480
},
"window": {
"pixelRatio": 2,
"orientation": 0,
"innerWidth": 320,
"screenHeight": 480,
"innerHeight": 403,
"screenWidth": 320
},
"html": {
"clientWidth": 320,
"offsetHeight": 0,
"scrollWidth": 320,
"clientHeight": 480,
"scrollHeight": 0
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"body": {
"offsetHeight": 0,
"scrollHeight": 480
},
"window": {
"pixelRatio": 2,
"orientation": 0,
"innerWidth": 320,
"screenHeight": 480,
"innerHeight": 403,
"screenWidth": 320
},
"html": {
"clientWidth": 320,
"offsetHeight": 0,
"scrollWidth": 320,
"clientHeight": 480,
"scrollHeight": 0
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"body": {
"offsetHeight": 0,
"scrollHeight": 480
},
"window": {
"pixelRatio": 2,
"orientation": 0,
"innerWidth": 320,
"screenHeight": 480,
"innerHeight": 403,
"screenWidth": 320
},
"html": {
"clientWidth": 320,
"offsetHeight": 0,
"scrollWidth": 320,
"clientHeight": 480,
"scrollHeight": 0
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"body": {
"offsetHeight": 0,
"scrollHeight": 480
},
"window": {
"pixelRatio": 2,
"orientation": 0,
"innerWidth": 320,
"screenHeight": 480,
"innerHeight": 403,
"screenWidth": 320
},
"html": {
"clientWidth": 320,
"offsetHeight": 0,
"scrollWidth": 320,
"clientHeight": 480,
"scrollHeight": 0
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion test/helper/compareImages.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default function compareImages(image1, image2, misMatchPercentage = 0.2)
return new Promise((resolve) => {
const image = resemble(image1).compareTo(image2);
image.onComplete((data) => {
assert.isTrue(data.isSameDimensions, `different dimensions, see "${image1}" and "${image2}"`);
assert.isTrue(data.isSameDimensions, `different dimensions ${JSON.stringify(data.dimensionDifference)}, see "${image1}" and "${image2}"`);
assert.closeTo(Number(data.misMatchPercentage), 0, misMatchPercentage, `different images, see "${image1}" and "${image2}"`);
resolve();
});
Expand Down
62 changes: 62 additions & 0 deletions test/unit/utils/normalizeScreenshot.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,66 @@ describe('normalizeScreenshot', function() {

});

context('Android', function() {
const androidDir = path.join(screenshotDir, 'android');
const files = glob.sync('**/screenshot.png', {cwd: androidDir});

const data = files.map((file) => {
const dir = path.dirname(file);
const [ version, device, test ] = _.times(2)
.reduce((f, v, i) => f.concat(path.dirname(f[f.length -1])), [dir])
.reverse()
.map((f) => path.basename(f).replace(/_/g, ' '))

return {
version,
device,
test,
screenshotFile: path.join(androidDir, file),
expectedScreenshotFile: path.join(androidDir, dir, 'expected.png'),
dimensionsFile: path.join(androidDir, dir, 'dimensions.json'),
skipFile: path.join(androidDir, dir, '.SKIP'),
dir,
};
});

const testData = _.mapValues(_.groupBy(data, "version"), (list) => _.groupBy(list, "device"));

_.mapKeys(testData, (devices, version) => {
context(version, function () {
_.mapKeys(devices, (list, device) => {
context(device, function () {
list.forEach(({ test, screenshotFile, expectedScreenshotFile, dimensionsFile, skipFile, dir }) => {
it(test, async function () {
const browser = {
isMobile: true,
isIOS: false,
isAndroid: true
};

const skip = await fsExtra.exists(skipFile);
if (skip) {
this.skip();
return;
}
const dimensions = await fsExtra.readJson(dimensionsFile);
const base64Screenshot = await readAsBase64(screenshotFile);
const screenDimensions = new ScreenDimension(dimensions, browser);

const normalizedSreenshot = await normalizeScreenshot(browser, screenDimensions, base64Screenshot);
const normalizedSreenshotPath = path.join(tmpPath, 'normalizeScreenshot', dir, 'normalized.png');
await saveBase64Image(normalizedSreenshotPath, normalizedSreenshot);
await readAsBase64(expectedScreenshotFile); // just to check if it exists

await compareImages(normalizedSreenshotPath, expectedScreenshotFile, 0.001);
});
});
});
});
});
});


});

});