diff --git a/core/src/utils/platform.ts b/core/src/utils/platform.ts index 7790577599..0353576924 100644 --- a/core/src/utils/platform.ts +++ b/core/src/utils/platform.ts @@ -35,6 +35,10 @@ const isIOS = (win: Window) => const isAndroid = (win: Window) => testUserAgent(win, /android|sink/i); +const isAndroidTablet = (win: Window) => { + return isAndroid(win) && !testUserAgent(win, /mobile/i); +}; + const isPhablet = (win: Window) => { const width = win.innerWidth; const height = win.innerHeight; @@ -50,8 +54,15 @@ const isTablet = (win: Window) => { const height = win.innerHeight; const smallest = Math.min(width, height); const largest = Math.max(width, height); - return (smallest > 460 && smallest < 820) && - (largest > 780 && largest < 1400); + + return ( + isIpad(win) || + isAndroidTablet(win) || + ( + (smallest > 460 && smallest < 820) && + (largest > 780 && largest < 1400) + ) + ); }; const isMobile = (win: Window) => diff --git a/core/src/utils/test/platform.spec.ts b/core/src/utils/test/platform.spec.ts index da72a1e72c..15cb3d0d77 100644 --- a/core/src/utils/test/platform.spec.ts +++ b/core/src/utils/test/platform.spec.ts @@ -1,121 +1,118 @@ import { testUserAgent, getPlatforms, isPlatform } from '../platform'; +import { PlatformConfiguration, configureBrowser } from './platform.utils'; -enum PlatformConfiguration { - AndroidTablet = { - navigator: { - userAgent: 'Mozilla/5.0 (Linux; Android 7.0; Pixel C Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.98 Safari/537.36' - }, - innerWidth: 800, - innerHeight: 1200 - }, - Capacitor = { - Capacitor: { - isNative: true - } - }, - Cordova = { - cordova: true - }, - DesktopSafari = { - navigator: { - userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9' - }, - innerWidth: 1920, - innerHeight: 1080 - }, - iPhone = { - navigator: { - userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1' - }, - innerWidth: 375, - innerHeight: 812 - } -} - -describe('Platform Tests', () => { - /** - * The `window` object is not reset - * after each test, so we need to do - * that manually otherwise tests will - * interefere with each other - */ - - let sharedWindow; - beforeEach(() => { - sharedWindow = Object.create(window); - }); - +describe('Platform Tests', () => { describe('testUserAgent()', () => { it('should return true when testing if user agent is an iPhone', () => { - configureBrowser(sharedWindow, PlatformConfiguration.iPhone); - expect(testUserAgent(sharedWindow, /iPhone/)).toEqual(true); + const win = configureBrowser(PlatformConfiguration.iPhone); + expect(testUserAgent(win, /iPhone/)).toEqual(true); }) it('should return false when testing if user agent is an iPad', () => { - configureBrowser(sharedWindow, PlatformConfiguration.iPhone); - expect(testUserAgent(sharedWindow, /iPad/)).toEqual(false); + const win = configureBrowser(PlatformConfiguration.iPhone); + expect(testUserAgent(win, /iPad/)).toEqual(false); }) it('should return false when testing if user agent is an Android', () => { - configureBrowser(sharedWindow, PlatformConfiguration.iPhone); - expect(testUserAgent(sharedWindow, /android|sink/i)).toEqual(false); + const win = configureBrowser(PlatformConfiguration.iPhone); + expect(testUserAgent(win, /android|sink/i)).toEqual(false); }) it('should return true when testing if user agent is an Android', () => { - configureBrowser(sharedWindow, PlatformConfiguration.AndroidTablet); - expect(testUserAgent(sharedWindow, /android|sink/i)).toEqual(true); + const win = configureBrowser(PlatformConfiguration.AndroidTablet); + expect(testUserAgent(win, /android|sink/i)).toEqual(true); }) }); describe('getPlatforms()', () => { it('should contain "desktop" platform', () => { - configureBrowser(sharedWindow, PlatformConfiguration.DesktopSafari); - expect(getPlatforms(sharedWindow)).toContain('desktop'); + const win = configureBrowser(PlatformConfiguration.DesktopSafari); + expect(getPlatforms(win)).toContain('desktop'); }); it('should contain "android" and "tablet" platforms', () => { - configureBrowser(sharedWindow, PlatformConfiguration.AndroidTablet); + const win = configureBrowser(PlatformConfiguration.AndroidTablet); - const platforms = getPlatforms(sharedWindow); + const platforms = getPlatforms(win); expect(platforms).toContain('android'); expect(platforms).toContain('tablet'); }) it('should contain "capacitor" platform', () => { - configureBrowser(sharedWindow, PlatformConfiguration.Capacitor); - expect(getPlatforms(sharedWindow)).toContain('capacitor'); + const win = configureBrowser(PlatformConfiguration.Capacitor); + expect(getPlatforms(win)).toContain('capacitor'); }) }); describe('isPlatform()', () => { it('should return true for "capacitor" and "hybrid" in a Capacitor app', () => { - configureBrowser(sharedWindow, PlatformConfiguration.Capacitor); - expect(isPlatform(sharedWindow, 'capacitor')).toEqual(true); - expect(isPlatform(sharedWindow, 'hybrid')).toEqual(true); + const win = configureBrowser(PlatformConfiguration.Capacitor); + expect(isPlatform(win, 'capacitor')).toEqual(true); + expect(isPlatform(win, 'hybrid')).toEqual(true); }); - it('should return false for "capacitor" on desktop safari', () => { - configureBrowser(sharedWindow, PlatformConfiguration.DesktopSafari); - expect(isPlatform(sharedWindow, 'capacitor')).toEqual(false); + it('should return false for "capacitor" and true for "desktop" on desktop safari', () => { + const win = configureBrowser(PlatformConfiguration.DesktopSafari); + expect(isPlatform(win, 'capacitor')).toEqual(false); + expect(isPlatform(win, 'desktop')).toEqual(true); }); it('should return true for "android" and "tablet" on an android tablet', () => { - configureBrowser(sharedWindow, PlatformConfiguration.AndroidTablet); - expect(isPlatform(sharedWindow, 'android')).toEqual(true); - expect(isPlatform(sharedWindow, 'tablet')).toEqual(true); + const win = configureBrowser(PlatformConfiguration.AndroidTablet); + expect(isPlatform(win, 'android')).toEqual(true); + expect(isPlatform(win, 'tablet')).toEqual(true); }); it('should return true for "cordova" and "hybrid" in a Cordova app', () => { - configureBrowser(sharedWindow, PlatformConfiguration.Cordova); - expect(isPlatform(sharedWindow, 'cordova')).toEqual(true); - expect(isPlatform(sharedWindow, 'hybrid')).toEqual(true); + const win = configureBrowser(PlatformConfiguration.Cordova); + expect(isPlatform(win, 'cordova')).toEqual(true); + expect(isPlatform(win, 'hybrid')).toEqual(true); + }); + + it('should return true for "ipad" and "tablet" on an iPad Pro', () => { + const win = configureBrowser(PlatformConfiguration.iPadPro); + expect(isPlatform(win, 'ipad')).toEqual(true); + expect(isPlatform(win, 'tablet')).toEqual(true); + }); + + it('should return true for "android", false for "tablet, and false for "desktop"" on a Pixel 2 XL', () => { + const win = configureBrowser(PlatformConfiguration.Pixel2XL); + expect(isPlatform(win, 'android')).toEqual(true); + expect(isPlatform(win, 'tablet')).toEqual(false); + expect(isPlatform(win, 'desktop')).toEqual(false); + }); + + it('should return true for "android" and "tablet" and false for "desktop" on a Galaxy View', () => { + const win = configureBrowser(PlatformConfiguration.GalaxyView); + expect(isPlatform(win, 'android')).toEqual(true); + expect(isPlatform(win, 'tablet')).toEqual(true); + expect(isPlatform(win, 'desktop')).toEqual(false); + }); + + it('should return false for "android" and "tablet" on desktop Safari', () => { + const win = configureBrowser(PlatformConfiguration.DesktopSafari); + expect(isPlatform(win, 'android')).toEqual(false); + expect(isPlatform(win, 'tablet')).toEqual(false); + }); + + it('should return false for "android" and "tablet" and false for "desktop" on iPhone', () => { + const win = configureBrowser(PlatformConfiguration.iPhone); + expect(isPlatform(win, 'android')).toEqual(false); + expect(isPlatform(win, 'tablet')).toEqual(false); + expect(isPlatform(win, 'desktop')).toEqual(false); + }); + + it('should return true for "android", false for "tablet", and false for "desktop" on Galaxy S9 Plus', () => { + const win = configureBrowser(PlatformConfiguration.GalaxyS9Plus); + expect(isPlatform(win, 'android')).toEqual(true); + expect(isPlatform(win, 'tablet')).toEqual(false); + expect(isPlatform(win, 'desktop')).toEqual(false); + }); + + it('should return true for "pwa" and false for "cordova"', () => { + const win = configureBrowser(PlatformConfiguration.PWA); + expect(isPlatform(win, 'pwa')).toEqual(true); + expect(isPlatform(win, 'cordova')).toEqual(false); }); - }) -}); - -function configureBrowser(win: Window, config: PlatformConfiguration): void { - for (let attributeKey in config) { - win[attributeKey] = config[attributeKey]; - } -} \ No newline at end of file +}); \ No newline at end of file diff --git a/core/src/utils/test/platform.utils.ts b/core/src/utils/test/platform.utils.ts new file mode 100644 index 0000000000..5e06d03c99 --- /dev/null +++ b/core/src/utils/test/platform.utils.ts @@ -0,0 +1,89 @@ +export const configureBrowser = (config: any, win: any = Object.create(window)) => { + for (const attributeKey in config) { + if (config.hasOwnProperty(attributeKey)) { + win[attributeKey] = config[attributeKey]; + } + } + + return win; +}; + +export const mockMatchMedia = (media: string[] = []) => { + return jest.fn().mockImplementation(query => { + return { + matches: media.includes(query) + }; + }); +}; + +export const PlatformConfiguration = { + AndroidTablet: { + navigator: { + userAgent: 'Mozilla/5.0 (Linux; Android 7.0; Pixel C Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.98 Safari/537.36' + }, + innerWidth: 800, + innerHeight: 1200, + matchMedia: mockMatchMedia(['(any-pointer:coarse)']) + }, + Capacitor: { + Capacitor: { + isNative: true + } + }, + PWA: { + navigator: { + standalone: true + }, + matchMedia: mockMatchMedia(['(display-mode: standalone)']) + }, + Cordova: { + cordova: true + }, + DesktopSafari: { + navigator: { + userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9' + }, + innerWidth: 1920, + innerHeight: 1080 + }, + iPhone: { + navigator: { + userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1' + }, + innerWidth: 375, + innerHeight: 812, + matchMedia: mockMatchMedia(['(any-pointer:coarse)']) + }, + iPadPro: { + navigator: { + userAgent: 'Mozilla/5.0 (iPad; CPU OS 12_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1' + }, + innerWidth: 1024, + innerHeight: 1366, + matchMedia: mockMatchMedia(['(any-pointer:coarse)']) + }, + Pixel2XL: { + navigator: { + userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Mobile Safari/537.36' + }, + innerWidth: 411, + innerHeight: 823, + matchMedia: mockMatchMedia(['(any-pointer:coarse)']) + }, + GalaxyView: { + navigator: { + userAgent: 'Mozilla/5.0 (Linux; Android 5.1.1; SM-T670 Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36' + }, + innerWidth: 1920, + innerHeight: 1080, + matchMedia: mockMatchMedia(['(any-pointer:coarse)']) + }, + GalaxyS9Plus: { + navigator: { + userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; SM-G965F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Mobile Safari/537.36' + }, + innerWidth: 360, + innerHeight: 740, + matchMedia: mockMatchMedia(['(any-pointer:coarse)']) + } +};