blog image

How to test angular mobile web app in android os from dev pc mockapi

To develop mobile web app was and is always challenging to make it work correctly with all browsers, platforms, os. With the spread of mobile devices the challenge gained complexity not only to browsers but also to device specific behavior based on os , version and hw. Many frameworks such as angular try to wrap up this complexity. We were extending mobile app with some validation functionality and had big challenge to cover all OS platforms such a Windows 10, iOS, android. The issue occured with android for the keypress, keyup, keydown events which are not fully supported.

In order to detect it and find a workaround we needed in the dev enviroments android emulator or device, which can for every itteration consume compiled changes in the dev mockapi.

I will descibe 2 approaches, which we believe are suitable :

1. Android studio

By installing android studio there is a device emulator. You can choose device platform, android version. Count with  high cpu and ram usage. If you are testing local mockapi dev , use proxy 10.0.2.2:8080 to reach your computer localhost. Works fine once you have a powerfull dev machine.

 as_deviceem

2.  Testing from you mobile device via USB on chrome browser

 This approach allows you access your dev machine content with your mobile device connected to usb via chrome browser. The steps include activate developer options on your android device and configure usb proxy in chrome

This allows you test on real physical device. After finishing your development make sure after deployment to test enviroment do proper testing with all targeted devices for your mobile web app.

P.S. 

We have described the field validation in angular  for specific license plate format in the switzerland.  This did not work on android devices, only in iOs and Windows. The events keypress , keyup, keydown, preventDefaults for event are not supported for reasons in android. Here is the version which works with all platforms thanks to described dev unit testing with android studio. The principle is to use input event and instead of predefault store the previous value in the variable and in case the input is not accepted replace it with previous value.

 import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';

import {ChatService} from '../chat.service';
import {ChatItemDef, InputPattern} from '../../../core/session.service';
import { fromEvent, Subscription } from 'rxjs';

@Component({
    selector: 'ph-chat-entry-text',
    templateUrl: 'chat-entry-text.component.html',
    styleUrls: ['./chat-entry-text.component.scss']
})
export class ChatEntryTextComponent implements OnInit {
    @ViewChild('entryText', {static: true}) entryInput: ElementRef;
    @Input() chatItemDef: ChatItemDef;
    @Output() focusChanged = new EventEmitter<string>();
    public showSubmit=true;
    public placeholder='';
    public isPlate=false;
    private regexPlateInput = '^[a-zA-Z]$|^[a-zA-Z]{2}[0-9]{0,6}$';
    private regexPlateSubmit = '^[a-zA-Z]{2}[0-9]+$';
    private platePrevValue='';
    private subscriptions: Subscription[] = [];

    constructor(private chatSvc: ChatService) {

    }
    onEnter(event) {
        if (this.isPlate) {
            if (this.onSubmitPlateValidation (event.target.value)) {
                this.onSubmit();
            }
            else {
                event.preventDefault();
                return false;
            }

        } else this.onSubmit();

    }
    onSubmitPlateValidation(str) {
        let regexSubmit = new RegExp(this.regexPlateSubmit);
        return regexSubmit.test(str);

    }
    onSubmit() {
        if (this.chatItemDef.value) {    // value must exist
            let value: string = this.chatItemDef.value;
            if (this.chatItemDef.pattern === InputPattern.NUMBERPLATE) {
                value = value.toUpperCase().replace(/\s/g, '');
            }
            value = value.trim();
            this.chatSvc.processAnswer(value, value);
            this.chatItemDef.value = '';
            this.chatSvc.currentInputType = null;
        }
    }

    onFocusChanged(type: string) {
        this.focusChanged.emit(type);
    }

    ngOnInit(): void {
        if (!this.chatSvc.isApple()) {
            const endPos = this.chatItemDef.value ? this.chatItemDef.value.length : 0;
            this.entryInput.nativeElement.setSelectionRange(endPos, endPos);    // set caret
            this.entryInput.nativeElement.focus();
        }
        //based on bug from Daniel , after remove this
        //this.chatItemDef.value = '';
        if (this.chatItemDef.pattern === InputPattern.NUMBERPLATE) {
            this.showSubmit=false;
            if (this.chatItemDef.value) {
                this.platePrevValue=this.chatItemDef.value;
                this.showSubmit = this.validateMaskPlate(this.chatItemDef.value);
            }
            this.isPlate=true;
            this.placeholder='BE12345';
            //subscriptions for events
            let subInput = new Subscription();
            let subPaste = new Subscription()
            const el = this.entryInput.nativeElement;
            subInput = fromEvent(el.parentNode, 'input', {capture: true})
                .subscribe((ev: any) => {
                    if (ev.target === el) {
                        this.validateKeyPress(ev);
                    }
                });
            //prevent paste for License plate field
            subPaste = fromEvent(el.parentNode, 'paste', {capture: true})
                .subscribe((ev: any) => {
                    if (ev.target === el) {
                        this.onPaste(ev);
                    }
                });
            this.subscriptions.push(subInput);
            this.subscriptions.push(subPaste);
        }
    }
    ngOnDestroy() {
            this.subscriptions.forEach((subscription) => subscription.unsubscribe());
    }

       validateKeyPress(event) {
        if (this.isPlate) {
             if (event.inputType === 'insertText' || event.inputType === 'insertCompositionText' || event.inputType === 'deleteContentBackward' || event.inputType === 'deleteContentForward') {
                if (this.validateMaskPlate(event.target.value)) {
                    this.platePrevValue = event.target.value;
                    return true;
                } else {
                    event.target.value = this.platePrevValue;
                    return false;
                }
            }
        }
        return true;

    }
    validateMaskPlate (str) {
            let regex = new RegExp(this.regexPlateInput);
            let regexSubmit = new RegExp(this.regexPlateSubmit);

            if (regex.test(str)||str.length ===0) {
                if (str.length>0)
                    this.showSubmit = regexSubmit.test(str);
                return true;
            } else {
                 return false;
            }
    }
    onPaste(e) {
        if (this.isPlate) {
            e.preventDefault();
            e.target.value = this.platePrevValue;
            return false;
        }
    }
    }

Skôr ako začneme: nahliadnite do spracovania vašich osobných údajov

Ak navštívite stránku, ktorá zapisuje cookies, v počítači sa vám vytvorí malý textový súbor, ktorý sa uloží vo vašom prehliadači. Ak rovnakú stránku navštívite nabudúce, pripojíte sa vďaka nemu na web rýchlejšie. Náš web vám ponúkne relevantné informácie a bude sa vám pracovať jednoduchšie.

Súbory cookies používame najmä na anonymnú analýzu návštevnosti a vylepšovanie našich web stránok. Ak si nastavíte blokovanie zápisu cookies do vášho prehliadača, je možné, že web sa spomalí a niektoré jeho časti nemusia fungovať úplne korektne. Viac info k spracúvaniu cookies.