2015-06-16 11 views
12

हम पृष्ठ वस्तु पैटर्न उपयोग कर रहे हैं हमारी आंतरिक AngularJS आवेदन टेस्ट का आयोजन करने के लिए। जब यह एक करने के लिए आता है,एकाधिक ब्राउज़र और पृष्ठ वस्तु पैटर्न

var LoginPage = require("./../po/login.po.js"); 

describe("Login functionality", function() { 
    var scope = {}; 

    beforeEach(function() { 
     browser.get("/#login"); 

     scope.page = new LoginPage(); 
    }); 

    it("should successfully log in a user", function() { 
     scope.page.username.clear(); 
     scope.page.username.sendKeys(login); 
     scope.page.password.sendKeys(password); 
     scope.page.loginButton.click(); 

     // assert we are logged in 
    }); 
}); 

लेकिन:,

var LoginPage = function() { 
    this.username = element(by.id("username")); 
    this.password = element(by.id("password")); 

    this.loginButton = element(by.id("submit")); 
} 

module.exports = LoginPage; 

एक एकल ब्राउज़र परीक्षण में यह काफी स्पष्ट है कि यह कैसे उपयोग करने के लिए:

यहाँ एक उदाहरण पेज वस्तु हमारे पास है परीक्षण करें जब एकाधिक ब्राउज़र तत्काल होते हैं और एक परीक्षण में उनके बीच स्विच करने की आवश्यकता होती है, यह स्पष्ट हो रहा है कि एक ही पेज ऑब्जेक्ट का उपयोग एकाधिक ब्राउज़रों के साथ कैसे किया जाए:

describe("Login functionality", function() { 
    var scope = {}; 

    beforeEach(function() { 
     browser.get("/#login"); 

     scope.page = new LoginPage(); 
    }); 

    it("should warn there is an opened session", function() { 
     scope.page.username.clear(); 
     scope.page.username.sendKeys(login); 
     scope.page.password.sendKeys(password); 
     scope.page.loginButton.click(); 

     // assert we are logged in 

     // fire up a different browser and log in 
     var browser2 = browser.forkNewDriverInstance(); 

     // the problem is here - scope.page.username.clear() would be applied to the main "browser" 
    }); 
}); 

समस्या:

हम एक नई ब्राउज़र काँटेदार के बाद, कैसे हम एक ही पृष्ठ वस्तु क्षेत्रों और कार्यों का उपयोग कर सकते हैं, लेकिन एक नए instantiated ब्राउज़र (इस मामले में browser2) के लिए आवेदन किया?

दूसरे शब्दों में, सभी element() यहां कॉल browser पर लागू होंगे, लेकिन browser2 पर लागू होने की आवश्यकता है। हम संदर्भ कैसे बदल सकते हैं?


विचार:

  • एक संभव यहाँ दृष्टिकोण अस्थाई रूप से browser2 के संदर्भ में किया जा रहा है redefine the global element = browser2.element होगा। इस दृष्टिकोण के साथ समस्या यह है कि पेज ऑब्जेक्ट फ़ंक्शंस के अंदर हमारे पास browser.wait() कॉल भी हैं। इसका मतलब है कि browser = browser2 भी सेट किया जाना चाहिए। इस मामले में, हम एक अस्थायी चर में browser वैश्विक वस्तु याद है और इसे फिर से स्थापित करने के लिए एक बार हम मुख्य browser संदर्भ में वापस स्विच .. पेज वस्तु में ब्राउज़र उदाहरण पारित करने के लिए की आवश्यकता होगी

  • एक अन्य संभावित दृष्टिकोण होगा , कुछ की तरह:

    var LoginPage = function (browserInstance) { 
        browser = browserInstance ? browserInstance : browser; 
        var element = browser.element; 
    
        // ... 
    } 
    

    लेकिन यह शायद हर पृष्ठ वस्तु हमारे पास बदलने के लिए की आवश्यकता होगी ..

आशा है कि सवाल स्पष्ट है - मुझे तो हमें बताएं इसे स्पष्टीकरण की आवश्यकता है।

+4

मैं अपने पृष्ठ वस्तु वर्ग उदाहरण के लिए अपने ड्राइवर सत्र गुजर तुलना में एक अधिक चालाक रास्ता नहीं सोच सकते हैं ... कि एक ही रास्ता मैं देख रहा हूँ आप अपने खुद के पैर के अंगूठे में कदम नहीं जब अनन्य कार्य के साथ काम कर देख सकते हैं एक ही परीक्षण में कई ब्राउज़र सत्रों में ... – Bodao

+0

@ user3087106 धन्यवाद, हमने वास्तव में इस दृष्टिकोण का उपयोग करना शुरू कर दिया है ... लेकिन यह सिर्फ 'ब्राउज़र इंस्टेंस' तर्क और प्रत्येक पृष्ठ ऑब्जेक्ट में दो पंक्तियों के साथ सुंदर और साफ दिखता नहीं है ऑब्जेक्ट हमारे पास है, मुझे लगता है कि हमें मूल पृष्ठ ऑब्जेक्ट "क्लास" होना चाहिए और DRY सिद्धांत का उल्लंघन करने से बचने के लिए 'प्रोटोटाइप' का उपयोग करना होगा .. – alecxe

+0

मैंने इसे निम्न स्तर की भाषाओं जैसे सी # और न्यूटिट, जावा और जुनीट, पायथन में किया है लेकिन मुझे शक है कि यह आपकी मदद करेगा। – Margus

उत्तर

3

देखो। मैंने उदाहरण को सरल बना दिया, लेकिन हम वर्तमान परियोजना में इस दृष्टिकोण का उपयोग कर रहे हैं। मेरे ऐप में उपयोगकर्ता अनुमति प्रकार दोनों के लिए पेज हैं, और मुझे दोनों ब्राउज़रों में एक ही समय में कुछ जटिल कार्य करने की आवश्यकता है। मुझे उम्मीद है कि यह आपको कुछ नया, बेहतर तरीका दिखा सकता है!

"use strict"; 

//In config, you should declare global browser roles. I only have 2 roles - so i make 2 global instances 
//Somewhere in onPrepare() function 
global.admin = browser; 
admin.admin = true; 

global.guest = browser.forkNewDriverInstance(); 
guest.guest = true; 

//Notice that default browser will be 'admin' example: 
// let someElement = $('someElement'); // this will be tried to be found in admin browser. 



class BasePage { 
    //Other shared logic also can be added here. 
    constructor (browser = admin) { 
     //Simplified example 
     this._browser = browser 
    } 
} 

class HomePage extends BasePage { 
    //You will not directly create this object. Instead you should use .getPageFor(browser) 
    constructor(browser) { 
     super(browser); 

     this.rightToolbar = ToolbarFragment.getFragmentFor(this._browser); 
     this.chat = ChatFragment.getFragmentFor(this._browser); 
     this.someOtherNiceButton = this._browser.$('button.menu'); 
    } 

    //This function relies on params that we have patched for browser instances in onPrepare(); 
    static getPageFor(browser) { 
     if (browser.guest) return new GuestHomePage(browser); 
     else if (browser.admin) return new AdminHomePage(browser); 
    } 

    openProfileMenu() { 
     let menu = ProfileMenuFragment.getFragmentFor(this._browser); 
     this.someOtherNiceButton.click(); 

     return menu; 
    } 
} 


class GuestHomePage extends RoomPage { 
    constructor(browser) { 
     super(browser); 
    } 

    //Some feature that is only available for guest 
    login() { 
     // will be 'guest' browser in this case. 
     this._browser.$('input.login').sendKeys('sdkfj'); //blabla 
     this._browser.$('input.pass').sendKeys('2345'); //blabla 
     this._browser.$('button.login').click(); 
    } 
} 


class AdminHomePage extends RoomPage { 
    constructor(browser) { 
     super(browser); 
    } 

    acceptGuest() { 
     let acceptGuestButton = this._browser.$('.request-admission .control-btn.admit-user'); 
     this._browser.wait(EC.elementToBeClickable(acceptGuestButton), 10000, 
       'Admin should be able to see and click accept guest button. ' + 
       'Make sure that guest is currently trying to connect to the page'); 

     acceptGuestButton.click(); 
     //Calling browser directly since we need to do complex action. Just example. 
     guest.wait(EC.visibilityOf(guest.$('.central-content')), 10000, 'Guest should be dropped to the page'); 
    } 

} 

//Then in your tests 
let guestHomePage = HomePage.getPageFor(guest); 
guestHomePage.login(); 
let adminHomePage = HomePage.getPageFor(admin); 
adminHomePage.acceptGuest(); 
adminHomePage.openProfileMenu(); 
guestHomePage.openProfileMenu(); 
4

शायद तुम ब्राउज़र पंजीकरण करने के लिए कुछ कार्यों लिखने// चिकनी स्विच शुरू कर सकता है। (मूल रूप से यह कुछ समर्थन के साथ अपना पहला विकल्प है।)

उदाहरण के लिए:

var browserRegistry = []; 

function openNewBrowser(){ 
    if(typeof browserRegistry[0] == 'undefined'){ 
    browseRegistry[0] = { 
     browser: browser, 
     element: element, 
     $: $, 
     $$: $$, 
     ... whatever else you need. 
    } 
    } 
    var tmp = browser.forkNewDriverInstance(); 
    var id = browserRegistry.length; 
    browseRegistry[id] = { 
     browser: tmp, 
     element: tmp.element, 
     $: tmp.$, 
     $$: tmp.$$, 
     ... whatever else you need. 
    } 
    switchToBrowserContext(id); 
    return id; 
} 
function switchToBrowserContext(id){ 
    browser=browseRegistry[id].browser; 
    element=browseRegistry[id].element; 
    $=browseRegistry[id].$; 
    $$=browseRegistry[id].$$; 
} 

और आप अपने उदाहरण में यह इस तरह का उपयोग करें:

describe("Login functionality", function() { 
    var scope = {}; 

    beforeEach(function() { 
     browser.get("/#login"); 
     scope.page1 = new LoginPage(); 
     openNewBrowser(); 
     browser.get("/#login"); 
     scope.page2 = new LoginPage(); 
    }); 

    it("should warn there is an opened session", function() { 
     scope.page1.username.clear(); 
     scope.page1.username.sendKeys(login); 
     scope.page1.password.sendKeys(password); 
     scope.page1.loginButton.click(); 

     scope.page2.username.clear(); 
     scope.page2.username.sendKeys(login); 
     scope.page2.password.sendKeys(password); 
     scope.page2.loginButton.click();  
    }); 
}); 

तो आप अपने पृष्ठ वस्तुओं छोड़ सकते हैं जैसे वो हे वैसे।

ईमानदारी से कहूं तो मुझे लगता है कि अपने दूसरे दृष्टिकोण क्लीनर है ... वैश्विक चर का उपयोग कर बाद में काट सकते हैं। लेकिन यदि आप अपने पीओ को बदलना नहीं चाहते हैं, तो यह भी काम कर सकता है।

(मैं इसे परीक्षण नहीं किया था ... संभावना लिखने की त्रुटियों/त्रुटियों के लिए क्षमा करें।) (उदाहरण के लिए आप अपने चांदा conf के onprepare अनुभाग के लिए सहायता कार्यों रख सकते हैं।) मेरी समाधान पर

+0

विकल्प के लिए धन्यवाद। मैं आपसे सहमत हूं - ब्राउज़र ऑब्जेक्ट को पेज ऑब्जेक्ट में पास करने के लिए और अधिक समझदारी होती है। मैं इसे सुंदर बनाने के लिए एक तरीका सोचूंगा और आशा करता हूं कि यहां भी पोस्ट किया जाएगा। – alecxe

+0

@alecxe वर्तमान में हम अपने अनुप्रयोगों में से किसी एक के लिए समान कार्यक्षमता को लागू करने की भी योजना बना रहे हैं। तो यह बहुत अच्छा होगा अगर आप मुझे सबसे अच्छे संभव दृष्टिकोण के साथ मार्गदर्शन कर सकते हैं। अग्रिम में धन्यवाद! –

संबंधित मुद्दे