NightWatchJS(轉)


關於Nightwatch?

Nightwatch.js是一個測試web app和web 站點的自動化測試框架, 使用Node.js編寫, 基於Selenium WebDriver API.

它是一個完整的瀏覽器端真實用戶場景測試解決方案, 致力於簡化繼續集成和編寫自動化測試。

Nightwatch got its name from the famous painting The Night Watch by Dutch painter Rembrandt van Rijn. The masterpiece is prominently displayed in the Rijksmuseum, in Amsterdam - The Netherlands.

Overview of Selenium

Selenium 是一個直接運行在瀏覽器中的非常流行的綜合測試工具集, 最初為Java 語言編寫, 現在已經支持許多語言。

Selenium的主要項目: 
Selenium IDE 
Selenium Remote Control 
Selenium WebDriver 
Selenium Grid

Nightwatch uses the Selenium WebDriver, specifically the WebDriver Wire Protocol to perform the browser automation related tasks.

Nightwatch 使用Selenium WebDriver, 特別是WebDriver Wire Protocol 來執行瀏覽器自動化測試相關任務。

Theory of Operation

Nightwatch works by sending HTTP requests to the Selenium server with the right parameters and interpreting the response. The restful API protocol is defined by the Selenium JsonWireProtocol. See below for an example workflow for browser initialization. 
Nightwatch 發送通過 HTTP 請求發送響應的參數到 Selenium 服務器,並解析服務器響應。restful API 文檔見Selenium JsonWireProtocol 。瀏覽器初始化流程見下圖。 
這里寫圖片描述

大部分情況下, Nightwatch 執行一條命令或斷言,至少要發送2個請求到 Selenium 服務器。 第一個用來定位元素,可以用 CSS 選擇器或者Xpath 表達式。 第二個用來在選中的元素上執行命令或斷言操作。

Installation

安裝Node.js

From nodejs.org:

“Node.js is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.”

主流操作系統的安裝包都可以在nodejs.org上找到。

Install Nightwatch

使用 npm 安裝最新的 Nightwatch, 在命令行運行:

npm install -g nightwatch

  

運行 Selenium 服務器

Selenium WebDriver server 是一個簡單的 Java servlet 程序,分別運行在本地機器上和你要進行測試的瀏覽器上。(todo 真的是瀏覽器上?)

Download Selenium 
Download the latest version of the selenium-server-standalone-{VERSION}.jar file from the Selenium downloads page and place it on the computer with the browser you want to test. In most cases this will be on your local machine and typically inside your project’s source folder. 
Selenium downloads page下載最新版的selenium-server-standalone-{VERSION}.jar 包, (todo)具體放在哪里, 等我試一試再說。 
也可以通過 npm 安裝 selenium server

npm install selenium-server

  

A good practice is to create a separate subfolder (e.g. bin) and place it there as you might have to download other driver binaries if you want to test multiple browsers.

Running Selenium Automatically 
如果服務器和Nightwatch 運行在同一台機器上, Nightwatch Test Runner可以啟動和停止Selenium 服務器。

Running Selenium Manually 
To run the selenium server manually, from the directory with the jar run the following: 
要手動啟動 server 服務器, 在 jar 包所在目錄執行:

java -jar selenium-server-standalone-{VERSION}.jar

  

更多關於運行 Selenium 服務器的信息參考: 
http://code.google.com/p/selenium/wiki/RemoteWebDriverServer

要獲取幫助信息, 執行:

java -jar selenium-server-standalone-{VERSION}.jar -help

  

Configuration

測試需要一個配置文件, 默認會使用當前目錄下的 nightwatch.json 文件。nightwatch.json 詳細配置如下:

{
  "src_folders" : ["tests"],
  "output_folder" : "reports",
  "custom_commands_path" : "",
  "custom_assertions_path" : "",
  "page_objects_path" : "",
  "globals_path" : "",

  "selenium" : {
    "start_process" : false,
    "server_path" : "",
    "log_path" : "",
    "host" : "127.0.0.1",
    "port" : 4444,
    "cli_args" : {
      "webdriver.chrome.driver" : "",
      "webdriver.ie.driver" : ""
    }
  },

  "test_settings" : {
    "default" : {
      "launch_url" : "http://localhost",
      "selenium_port"  : 4444,
      "selenium_host"  : "localhost",
      "silent": true,
      "screenshots" : {
        "enabled" : false,
        "path" : ""
      },
      "desiredCapabilities": {
        "browserName": "firefox",
        "javascriptEnabled": true,
        "acceptSslCerts": true
      }
    },

    "chrome" : {
      "desiredCapabilities": {
        "browserName": "chrome",
        "javascriptEnabled": true,
        "acceptSslCerts": true
      }
    }
  }
}

  

Basic settings

Name type default description
src_folders string|array none 測試代碼目錄(不包含子目錄)
output_folder (optional) string tests_output 生成的測試報告存放目錄
custom_commands_path (optional) string|array none Location(s) where custom commands will be loaded from.
custom_assertions_path (optional) string|array none 自定義斷言路徑
page_objects_path (Optional since v6.0.1) string|array none Location(s) where page object files will be loaded from.
globals_path (optional) string none 外部模塊路徑,為測試一共全局變量. 也可在test_settings中重寫
selenium (optional) object   Selenium Server相關的設置
test_settings (optional) object   與測試相關的測試,下面有詳細描述
live_output (optional) boolean false 是否緩存並持續輸出結果
disable_colors (optional) boolean false 控制命令行輸出是否帶有高亮顏色
parallel_process_delay (optional) integer 10 在並行模式下啟動子進程的時間,單位毫秒
test_workers boolean|object false 是否為運行單個文件測試啟動並行模式,如果設置為true,會進入並行模式,並自動設置線程數。如果設置為對象,要指定enable和workers參數,workers接受數值和’auto’。 例如:”test_workers” : {“enabled” : true, “workers” : “auto”}
test_runner (optional) string|object “default” 用什么工具運行測試。值可以是”default”或”mocha”. 例如:”test_runner” : {“type” : “mocha”, “options” : {“ui” : “tdd”}}

Selenium settings

如下是 selenium 的配置選項。Nightwatch可以自動管理 selenium 服務進程,以便你專注於測試工作。 
如果要啟用 selenium自啟動,設置 start_process 為true 並設置 server_path 值為 selenium jar包路徑。

Name type default description
start_process boolean false 是否啟用 selenium自啟動
start_session boolean true 是否自動啟動 Selenium session.
server_path string none selenium jar 包路徑。如果設置了 start_process 字段,必須設置此字段。例如: node_modules/selenium-server/lib/runner/selenium-server-standalone-2.53.0.jar
log_path string|boolean none selenium 生成的 output.log 文件存放位置,默認為當前目錄下。設置false可以禁用 Selenium 日志。
host string 127.0.0.1 設置 selenium 監聽地址。不常用到,除非指定了 start_process 為true。
port integer 4444 設置 selenium 監聽端口
cli_args object none 一系列傳遞給 selenium 的命令行參數。 有許多選項可以設置, 例如:
      webdriver.firefox.profile: 默認情況下會為每個會話創建一個firefox配置文件,如果想要使用現有配置文件,在這里指定文件名。firefox驅動參數的完整列表見這里 。
      webdriver.chrome.driver: Nightwatch 也可以在 Chrome 上運行測試,如果要在 chrome 上運行測試需要下載 ChromeDriver binary 並為此字段設置 ChromeDriver 路徑。同時要在desiredCapabilities 對象配置中指定 browserName值為 “chrome”。 更多信息參考ChromeDriver website
      webdriver.ie.driver: Nightwatch同時也支持Internet Explorer,要在IE中運行測試,需要下載 IE Driver binary 並為此字段設置 IE Driver 路徑。同時要在 desiredCapabilities 對象配置中指定 browserName值為 “internet explorer”。

Test settings

下面是一些Nightwatch 實例的配置信息。可以配置多個不同的配置對象,模擬多種測試環境。:

{
  ...
  "test_settings" : {
    "default" : {
      ...
    },
    "integration" : {
      ...
    }
  }
}

  

 

只有”default”配置是必須的,其他配置可以按需要覆蓋default中的配置信息。 
測試環境可以通過 -env 參數傳遞給 nightwatch:

nightwatch --env integration

  

 

Name type default description
launch_url string none 測試時要加載的首頁url, 如果有多個測試環境,可以分別指定url。
selenium_host string localhost 指定 selenium server 接受的 hostname/IP。
selenium_port string 4444 指定 selenium server 接受的端口。
silent boolean true 是否顯示 selenium 命令日志。
output boolean rue 是否在命令行顯示完整輸出。
disable_colors boolean false 命令行輸出是否高亮。
firefox_profile string|boolean none 已經棄用
chrome_driver string none 已經棄用
ie_driver string none 已經棄用
screenshots object none 當發生錯誤時 Selenium 會生成屏幕截圖。如果 on_failure 設置為 true, 發生錯誤或沒有通過測試時也生成屏幕截圖。
      從 v0.7.5 版本開始,可以為”on_error”字段設置false來禁止錯誤時生成截圖。
      例如:“screenshots”:{“enabled”:true,”on_failure”:true,”on_error”: false,”path”: “”}
username string none 萬一 selenium 需要憑證,該字段用來生成 Authorization header。值可以是系統變量,例如:”username” : “${SAUCE_USERNAME}”
access_key string none 與 username 一樣用於生成 Authorization header。像 username 一樣,值也可以是系統變量。
proxy string none 使用代理訪問 selenium server。支持 http, https, socks(v5), socks5, sock4, 和 pac。使用node-proxy-agent。Example: http://user:pass@host:port
desiredCapabilities object   在新建 session 之前傳遞給 Selenium WebDriver,可以用來指定瀏覽器名稱和其他功能。例如:“desiredCapabilities” : {“browserName” : “firefox”, “acceptSslCerts” :true}。 完整的功能列表在這里
globals object   在測試代碼中可以訪問的全局變量,並且每次切換測試環境時可以重寫該值。例如: *”globals” :{“myGlobal” : “some_global”}
exclude array   不包含的文件夾,接受字符串或模式字符串(relative to the main source folder)。例如:”exclude” : [“excluded-folder”] 或 :”exclude” : [“test-folder/*-smoke.js”]
filter string   接受字符串或模式字符串,與之不匹配的文件會被忽略。
log_screenshot_data boolean false 是否在日志(verbose模式)中記錄屏幕截圖的base64編碼信息。
use_xpath boolean false 是否用 xpath 做為默認的元素選擇策略。
cli_args object none 作用與 selenium 配置中的 cli_args 相同。 你可以在不同的測試環境配置中覆蓋 selenium 中的配置。
end_session_on_fail boolean true 在測試終止的時候,自動關閉會話,通常會在斷言失敗后觸發。
skip_testcases_on_fail boolean true 是否在任意測試用例測試失敗后,跳過剩余的測試用例。
output_folder string|boolean   生成的測試報告存放目錄, 該值覆蓋 Basic settings 中的配置, 也可以設置為 false 不生成報告。
persist_globals boolean false Weather or not to persist use the same object instance for holding globals between testsuite runs or a (deep) copy of it.

Using Nightwatch

Writing Tests

通過css選擇器在頁面中定位元素,Nightwatch 使得編寫 End-to-End 自動測試非常簡單。 
在你的項目中新建一個文件夾,比如tests,其中的文件會被 NightwatchCreate 加載並運行測試,一個最基本的測試如下所示:

module.exports = {
  'Demo test Google' : function (browser) {
    browser
      .url('http://www.google.com')
      .waitForElementVisible('body', 1000)
      .setValue('input[type=text]', 'nightwatch')
      .waitForElementVisible('button[name=btnG]', 1000)
      .click('button[name=btnG]')
      .pause(1000)
      .assert.containsText('#main', 'Night Watch')
      .end();
  }
};

  

 

當你想要停止測試時注意總是要調用 end() 方法,如此 selenium 會話才會正確的停止。

如果需要,一段測試也可以分為多步:

module.exports = {
  'step one' : function (browser) {
    browser
      .url('http://www.google.com')
      .waitForElementVisible('body', 1000)
      .setValue('input[type=text]', 'nightwatch')
      .waitForElementVisible('button[name=btnG]', 1000)
  },

  'step two' : function (browser) {
    browser
      .click('button[name=btnG]')
      .pause(1000)
      .assert.containsText('#main', 'Night Watch')
      .end();
  }
};

  

 

測試代碼也可以以這種格式來寫:

this.demoTestGoogle = function (browser) {
  browser
    .url('http://www.google.com')
    .waitForElementVisible('body', 1000)
    .setValue('input[type=text]', 'nightwatch')
    .waitForElementVisible('button[name=btnG]', 1000)
    .click('button[name=btnG]')
    .pause(1000)
    .assert.containsText('#main', 'The Night Watch')
    .end();
};

  

 

Using XPath selectors

Nightwatch 也支持 xpath。 調用 useXpath() 使用 xpath 規則選取元素。要退回 css 規則, 再調用 useCss() 方法。 
如果要用 xpath 做為默認的元素選擇策略,在配置文件中設置 “use_xpath”為true。

this.demoTestGoogle = function (browser) {
  browser
    .useXpath() // every selector now must be xpath
    .click("//tr[@data-recordid]/span[text()='Search Text']")
    .useCss() // we're back to CSS now
    .setValue('input[type=text]', 'nightwatch')
};

  

 

BDD Expect Assertions

從 Nightwatch0.7 版本開始, 引入了新的 BDD風格的斷言庫,大大提升了靈活性和代碼的可讀性。 
expect 斷言是 chai 框架 expect api 的子集,在此對元素類型也能使用。 例如:

module.exports = {
  'Demo test Google' : function (client) {
    client
      .url('http://google.no')
      .pause(1000);

    // expect element  to be present in 1000ms
    client.expect.element('body').to.be.present.before(1000);

    // expect element <#lst-ib> to have css property 'display'
    client.expect.element('#lst-ib').to.have.css('display');

    // expect element  to have attribute 'class' which contains text 'vasq'
    client.expect.element('body').to.have.attribute('class').which.contains('vasq');

    // expect element <#lst-ib> to be an input tag
    client.expect.element('#lst-ib').to.be.an('input');

    // expect element <#lst-ib> to be visible
    client.expect.element('#lst-ib').to.be.visible;

    client.end();
  }
};

  

 

expect 接口為斷言提供了靈活流暢的短語,比之前的接口有顯著的改善。 唯一的缺點是沒法鏈式調用斷言,並且自定義消息當前也不支持。

except 完整的 api 點這里

Using before[Each] and after[Each] hooks

Nightwatch 為測試提供了 before/after, beforeEach/afterEach 鈎子。 
before 和 after 鈎子會在每個測試套件運行前后執行。beforeEach 和 afterEach 則會在每個測試用例(測試步驟)執行前后執行。 
所有鈎子函數都接收一個 NightWatch 實例做為參數。 
Example:

module.exports = {
  before : function(browser) {
    console.log('Setting up...');
  },

  after : function(browser) {
    console.log('Closing down...');
  },

  beforeEach : function(browser) {

  },

  afterEach : function(browser) {

  },

  "step one" : function (browser) {
    browser
     // ...
  },

  "step two" : function (browser) {
    browser
    // ...
      .end();
  }
};

  

 

上例中方法的調用順序如下: 
before(), beforeEach(), “step one”, afterEach(), beforeEach(), “step two”, afterEach(), after()

Asynchronous before[Each] and after[Each]

所有的 before[Each] 和 after[Each] 鈎子函數對異步操作同樣適用,用於異步操作時會給函數傳遞第二個參數 callback .

done 方法必須在異步操作完成時之行,否則會拋出一個 timeOut 錯誤.

Example:

module.exports = {
  beforeEach: function(browser, done) {
    // performing an async operation
    setTimeout(function() {
      // finished async duties
      done();
    }, 100);
  },

  afterEach: function(browser, done) {
    // performing an async operation
    setTimeout(function() {
      // finished async duties
      done();
    }, 200);
  }
};

  

 

指定調用 done 方法的超時時間

默認情況下,done 方法的超時時間設置為 10 秒(2秒用來之行單元測試)。在某些情況下,10 秒鍾不夠而且會出現 timeOut 錯誤。 你可以在全局變量配置文件中自己設置 asyncHookTimeout 屬性來指定該值,單位為毫秒。 例子

強制測試失敗

要強制使測試失敗, 需要在調用 done 方法時,傳入一個 Error 對象。

module.exports = {
  afterEach: function(browser, done) {
    // performing an async operation
    performAsync(function(err) {
      if (err) {
        done(err);
      }
      // ...
    });
  }
};

  

 

用外部文件定義全局變量

大多數時候,使用外部文件定義全局變量是比較好的實踐。為 globals_path 屬性指定外部文件路徑, 而不是在 nightwatch.json 配置中定義全局變量。

還可以在應用每個 environment 之前重寫外部文件中的變量值。

全局鈎子

之前提到的鈎子函數在全局范圍也同樣適用,outside the scope of the test。下面的例子給出了詳細信息。 在這種情況下,beforeEach 和 afterEach 作用於一個測試集(i.e. test file)。

全局設置

globals文件有許多屬性用於控制測試流程。下面的例子給出了詳細信息:

module.exports = {
  // this controls whether to abort the test execution when an assertion failed and skip the rest
  // it's being used in waitFor commands and expect assertions
  abortOnAssertionFailure : true,

  // this will overwrite the default polling interval (currently 500ms) for waitFor commands
  // and expect assertions that use retry
  waitForConditionPollInterval : 300,

  // default timeout value in milliseconds for waitFor commands and implicit waitFor value for
  // expect assertions
  waitForConditionTimeout : 5000,

  // this will cause waitFor commands on elements to throw an error if multiple
  // elements are found using the given locate strategy and selector
  throwOnMultipleElementsReturned : true,

  // controls the timeout time for async hooks. Expects the done() callback to be invoked within this time
  // or an error is thrown
  asyncHookTimeout : 10000,

  'default' : {
    myGlobal : function() {
      return 'I\'m a method';
    }
  },

  'test_env' : {
    myGlobal: 'test_global',
    beforeEach : function() {

    }
  },

  // External before hook is ran at the beginning of the tests run, before creating the Selenium session
  before: function(done) {
    // run this only for the local-env
    if (this.isLocal) {
      // start the local server
      App.startServer(function() {
        // server listening
        done();
      });
    } else {
      done();
    }
  },

  // External after hook is ran at the very end of the tests run, after closing the Selenium session
  after: function(done) {
    // run this only for the local-env
    if (this.isLocal) {
      // start the local server
      App.stopServer(function() {
        // shutting down
        done();
      });
    } else {
      done();
    }
  },

  // This will be run before each test suite is started
  beforeEach: function(browser, done) {
    // getting the session info
    browser.status(function(result) {
      console.log(result.value);
      done();
    });
  },

  // This will be run after each test suite is finished
  afterEach: function(browser, done) {
    console.log(browser.currentTest);
    done();
  }
};

  

 

運行測試

Test Runner

Nightwatch 包含了一個命令行工具,可以非常簡單的運行測試並輸出有用的信息。根據你安裝的方式,有多種不同的命令形式。

Global

如果你是全局安裝 Nightwatch (使用 -g 選項),nightwatch 在所有目錄下都是可用的。

$ nightwatch [source] [options]

  

 

Project specific

如果 Nightwatch 是以工程的依賴形式安裝, 要在 node_modules/.bin/ 目錄下才能使用 nightwatch 命令。

Linux and MacOSX:

$ ./node_modules/.bin/nightwatch [source] [options]

  

 

Windows: 
創建一個 nightwatch.js 文件並輸入下面這行代碼:

require('nightwatch/bin/runner.js');

  

 

然后執行:

C:\workspace\project> node nightwatch.js [source] [options]

  

 

Tests source 
可選的 source 選項可以指定一個或多個文件,也可以直接指定一個文件夾,該選項獨立於 src_folders 設置。

Example - single test:

$ nightwatch tests/one/firstTest.js

  

 

Example - 2 individual tests:

$ nightwatch tests/one/firstTest.js tests/secondTest.js

  

 

Example - 1 individual test and 1 folder:

$ nightwatch tests/one/test.js tests/utils

  

命令行選項

Name Shortname default description
–config -c ./nightwatch.json nightwatch.json 文件路徑
–output -o tests_output JUnit 生成的報告保存路徑.
–reporter -r junit 指定預定義的生成報告的工具,或是自定義用於生成報告的文件路徑
–env -e default 指定測試環境,可以覆蓋 nightwatch.json 中的值
–verbose     是否在命令行輸出詳細信息
–version -v   是否顯示版本號
–test -t   運行指定的測試文件,默認會運行跟目錄下所有測試文件
–testcase     與test選項一同使用,指定要運行的測試用例
–group -g   運行指定組中(子目錄)中的所有測試,測試文件按目錄分組
–skipgroup -s   跳過指定的一個或多個(以逗號分隔)測試組
–filter -f   指定一個模式字符串用於過濾測試文件
–tag -a   使用tag過濾測試模塊,只有帶有相應標簽的測試模塊才被執行
–skiptags     指定跳過帶有相應標簽的模塊,多個以逗號隔開
–retries     Retries failed or errored testcases up to the specified number of times. Retrying a testcase will also retry the beforeEach and afterEach hooks, if any.
–suiteRetries     Retries failed or errored testsuites (test modules) up to the specified number of times. Retrying a testsuite will also retry the before and after hooks (in addition to the global beforeEach and afterEach respectively), if any are defined on the testsuite.

測試集

Nightwatch 允許你以集合的形式組織測試腳本, 並按需運行這些測試集。要把測試文件放在同一文件夾下就可以將它們組合起來,文件夾的名稱就是測試集的名稱。

Example:

lib/
  ├── selenium-server-standalone.jar
custom-commands/
  ├── loginUser.js
  ├── attachPicture.js
tests/
  ├── logingroup
  |   ├── login_test.js
  |   └── otherlogin_test.js
  ├── addressbook
  |   ├── addressbook_test.js
  |   └── contact_test.js
  ├── chat
  |   ├── chatwindow_test.js
  |   ├── chatmessage_test.js
  |   └── otherchat_test.js
  └── smoketests
      ├── smoke_test.js
      └── othersmoke_test.js

  

如果只想測試 smoketests , 運行如下命令:

$ nightwatch --group smoketests

  

如果要跳過 smoketests, 運行如下命令:

$ nightwatch --skipgroup smoketests

  

要跳過多個測試集, 以逗號分隔:

$ nightwatch --skipgroup addressbook,chat

  

標簽

你也可以選擇用標簽來管理要運行哪些測試, 那樣的話,一個測試可能屬於多個標簽。例如:有一個 login 測試即屬於 login 標簽,又屬於 sanity 標簽。

module.exports = {
  '@tags': ['login', 'sanity'],
  'demo login test': function (client) {
     // test code
  }
};

  

使用 –flag 命令行選項決定運行那個標簽下的測試:

$ nightwatch --tag login

 

指定多個標簽:

$ nightwatch --tag login --tag something_else

 

要跳過指定標簽, 使用 –skiptags 選項:

$ nightwatch --skiptags login

 

要跳過多個標簽, 用逗號隔開:

$ nightwatch --skiptags login,something_else

 

禁用測試

要阻止某個模塊執行測試, 只需要設置 disable 屬性為true :

module.exports = {
  '@disabled': true, // This will prevent the test module from running.

  'sample test': function (client) {
    // test code
  }
};

  

 

在明確某些模塊會測試失敗時,這很有用。

禁用單獨測試用例 
禁用模塊中單獨的測試用例,目前還不支持。 但是有一個變通方法, 將測試方法轉化為字符串,Nightwatch 就會忽略它。

module.exports = {
  'sample test': function (client) {
    // test code
  },

  // disabled
  'other sample test': ''+function (client) {
    // test code
  }
};

  

 

並行運行

從 v0.5 開始, Nightwatch 支持同時在多個環境中執行測試, 給 -e 參數指定多個值即可:

$ nightwatch -e default,chrome

 

終端輸出

每個測試環境以一個子進程運行, 而輸出信息會匯集到主進程。 
為了使輸出信息更易閱讀, Nightwatch 默認會緩存所有輸出,在最后再以每個環境為一組,一起輸出這些信息。

如果你想要禁止緩存輸出,在子進程的輸出發送到 stdout 后立即可見。需要在 nightwatch.json 文件最外層(e.g selenium屬性之后) 中設置 live_output 屬性為true。

You can create a separate environment per browser (by chaining desiredCapabilities) and then run them in parallel. In addition, using the filter and exclude options tests can be split per environment in order to be ran in parallel.

Via Workers

從 v0.7 開始引入了一個新特性,


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM