组织和执行Selenium代码

组织和执行Selenium代码

组织和执行Selenium代码使用IDE和Test Runner库组织Selenium的执行如果你不仅仅只是想执行一小撮的一次性脚本,你需要能组织并编排好你的代码。

本章会启发你如何真正地使用 Selenium 代码做高效的事情。

常见用法大部分人使用 Selenium 执行针对 Web 应用的自动化测试,但是 Selenium 其实可以支持任何场景的浏览器自动化。

重复性任务有时候你需要往网站记录日志或者下载一些东西,或者提交一个表单,

你可以在预设的时间创建一个 Selenium 脚本去执行一个服务。

网页爬虫你是否期望从一个不提供 API 的网站收集数据?Selenium 可以满足你,

但是请确保你了解该网站的服务条例,

因为有些网站不允许你这样做,甚至有些网站会屏蔽 Selenium。

测试使用 Selenium 做测试需要在 Selenium 执行操作后进行断言,所以一个好的断言类库是很有必要的。

至于组织测试用例结构的一些额外特性则需要Test Runner来完成。

IDEs不管你要用 Selenium 来做什么,没有一个好的集成开发环境,你的工作肯定不会高效。以下是一些常见的 IDE 选择:

EclipseIntelliJ IDEAPyCharmRubyMineRiderWebStormVS CodeTest Runner即使不使用 Selenium 做测试,如果你有高级用例,使用一个 test runner 去更好地组织你的代码是很有意义的。

学会使用 before/after hooks 和分组执行或者并行执行将会非常有用。

候选有非常多不同的 test runner 可供选择。

这个教程中所有使用到 test runner 的代码示例都可以在我们的示例目录中找到(或者正在被迁移过去),

而且这些示例在每一次发版都会被执行,以确保代码是正确的和最新的。

下面是一份包含对应链接的 test runner 清单,其中第一项是被这个仓库和本页所有用例所使用的。

Java

Python

CSharp

Ruby

JavaScript

KotlinJUnit - 一个广泛使用的用于基于 Java 的 Selenium 测试的测试框架。TestNG - 提供诸如并行测试执行和参数化测试等额外功能。pytest - 由于其简单性和强大的插件,它成为许多人的首选。unittest - Python 的标准测试库NUnit - .NET的流行单元测试框架MS Test - 微软自己的单元测试框架RSpec - Ruby中运行Selenium测试最广泛使用的测试库Minitest - 一个随Ruby标准库附带的轻量级测试框架Jest - 主要作为React的测试框架而闻名,但也可以用于Selenium测试Mocha -最常用的运行Selenium测试的JavaScript库。Kotest - 一个灵活且全面的测试框架,专为 Kotlin 设计。JUnit5 - 标准的 Java 测试框架,完全兼容 Kotlin。安装在安装 Selenium 类库一节中详细说明了需要哪些东西。

这里的代码只展示在我们的文档示例项目中用到的示例。

Java

Python

CSharp

Ruby

JavaScript

KotlinMaven

Gradle

To use it in a project, add it to the requirements.txt file:

in the project’s csproj file, specify the dependency as a PackageReference in ItemGroup:

Add to project’s gemfile

In your project’s package.json, add requirement to dependencies:

断言

Java

Python

CSharp

Ruby

JavaScript

Kotlin String title = driver.getTitle();

assertEquals("Web form", title);View Complete Code

View on GitHub/examples/java/src/test/java/dev/selenium/getting_started/UsingSeleniumTest.java

Copy

Closepackage dev.selenium.getting_started;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.time.Duration;

import org.junit.jupiter.api.AfterEach;

import org.junit.jupiter.api.BeforeEach;

import org.junit.jupiter.api.Test;

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.chrome.ChromeDriver;

public class UsingSeleniumTest {

WebDriver driver;

@BeforeEach

public void setup() {

driver = new ChromeDriver();

}

@Test

public void eightComponents() {

driver.manage().timeouts().implicitlyWait(Duration.ofMillis(500));

driver.get("https://www.selenium.dev/selenium/web/web-form.html");

String title = driver.getTitle();

assertEquals("Web form", title);

WebElement textBox = driver.findElement(By.name("my-text"));

WebElement submitButton = driver.findElement(By.cssSelector("button"));

textBox.sendKeys("Selenium");

submitButton.click();

WebElement message = driver.findElement(By.id("message"));

String value = message.getText();

assertEquals("Received!", value);

}

@AfterEach

public void teardown() {

driver.quit();

}

}

title = driver.title

assert title == "Web form"View Complete Code

View on GitHub/examples/python/tests/getting_started/using_selenium_tests.py

Copy

Closefrom selenium import webdriver

from selenium.webdriver.common.by import By

def test_eight_components():

driver = setup()

title = driver.title

assert title == "Web form"

driver.implicitly_wait(0.5)

text_box = driver.find_element(by=By.NAME, value="my-text")

submit_button = driver.find_element(by=By.CSS_SELECTOR, value="button")

text_box.send_keys("Selenium")

submit_button.click()

message = driver.find_element(by=By.ID, value="message")

value = message.text

assert value == "Received!"

teardown(driver)

def setup():

driver = webdriver.Chrome()

driver.get("https://www.selenium.dev/selenium/web/web-form.html")

return driver

def teardown(driver):

driver.quit()

var title = driver.Title;

Assert.AreEqual("Web form", title);View Complete Code

View on GitHub/examples/dotnet/SeleniumDocs/GettingStarted/UsingSeleniumTest.cs

Copy

Closeusing System;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using OpenQA.Selenium;

using OpenQA.Selenium.Chrome;

namespace SeleniumDocs.GettingStarted

{

[TestClass]

public class UsingSeleniumTest

{

[TestMethod]

public void EightComponents()

{

IWebDriver driver = new ChromeDriver();

driver.Navigate().GoToUrl("https://www.selenium.dev/selenium/web/web-form.html");

var title = driver.Title;

Assert.AreEqual("Web form", title);

driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(500);

var textBox = driver.FindElement(By.Name("my-text"));

var submitButton = driver.FindElement(By.TagName("button"));

textBox.SendKeys("Selenium");

submitButton.Click();

var message = driver.FindElement(By.Id("message"));

var value = message.Text;

Assert.AreEqual("Received!", value);

driver.Quit();

}

}

} title = @driver.title

expect(title).to eq('Web form')View Complete Code

View on GitHub/examples/ruby/spec/getting_started/using_selenium_spec.rb

Copy

Close# frozen_string_literal: true

require 'spec_helper'

require 'selenium-webdriver'

RSpec.describe 'Using Selenium' do

before do

@driver = Selenium::WebDriver.for :chrome

end

it 'uses eight components' do

@driver.get('https://www.selenium.dev/selenium/web/web-form.html')

title = @driver.title

expect(title).to eq('Web form')

@driver.manage.timeouts.implicit_wait = 500

text_box = @driver.find_element(name: 'my-text')

submit_button = @driver.find_element(tag_name: 'button')

text_box.send_keys('Selenium')

submit_button.click

message = @driver.find_element(id: 'message')

value = message.text

expect(value).to eq('Received!')

end

end

let title = await driver.getTitle();

assert.equal("Web form", title);View Complete Code

View on GitHub/examples/javascript/test/getting_started/runningTests.spec.js

Copy

Closeconst {By, Builder} = require('selenium-webdriver');

const assert = require("assert");

describe('First script', function () {

let driver;

before(async function () {

driver = await new Builder().forBrowser('chrome').build();

});

it('First Selenium script with mocha', async function () {

await driver.get('https://www.selenium.dev/selenium/web/web-form.html');

let title = await driver.getTitle();

assert.equal("Web form", title);

await driver.manage().setTimeouts({implicit: 500});

let textBox = await driver.findElement(By.name('my-text'));

let submitButton = await driver.findElement(By.css('button'));

await textBox.sendKeys('Selenium');

await submitButton.click();

let message = await driver.findElement(By.id('message'));

let value = await message.getText();

assert.equal("Received!", value);

});

after(async () => await driver.quit());

}); val title = driver.title

assertEquals("Web form", title)View Complete Code

View on GitHub/examples/kotlin/src/test/kotlin/dev/selenium/getting_started/FirstScriptTest.kt

Copy

Closepackage dev.selenium.getting_started

import org.junit.jupiter.api.*

import org.junit.jupiter.api.Assertions.assertEquals

import org.openqa.selenium.By

import org.openqa.selenium.WebDriver

import org.openqa.selenium.chrome.ChromeDriver

import java.time.Duration

@TestInstance(TestInstance.Lifecycle.PER_CLASS)

class FirstScriptTest {

private lateinit var driver: WebDriver

@Test

fun eightComponents() {

driver = ChromeDriver()

driver.get("https://www.selenium.dev/selenium/web/web-form.html")

val title = driver.title

assertEquals("Web form", title)

driver.manage().timeouts().implicitlyWait(Duration.ofMillis(500))

var textBox = driver.findElement(By.name("my-text"))

val submitButton = driver.findElement(By.cssSelector("button"))

textBox.sendKeys("Selenium")

submitButton.click()

val message = driver.findElement(By.id("message"))

val value = message.getText()

assertEquals("Received!", value)

driver.quit()

}

}Setting Up and Tearing Down

Java

Python

CSharp

Ruby

JavaScript

KotlinSet Up @BeforeEach

public void setup() {

driver = new ChromeDriver();

}View Complete Code

View on GitHub/examples/java/src/test/java/dev/selenium/getting_started/UsingSeleniumTest.java

Copy

Closepackage dev.selenium.getting_started;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.time.Duration;

import org.junit.jupiter.api.AfterEach;

import org.junit.jupiter.api.BeforeEach;

import org.junit.jupiter.api.Test;

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.chrome.ChromeDriver;

public class UsingSeleniumTest {

WebDriver driver;

@BeforeEach

public void setup() {

driver = new ChromeDriver();

}

@Test

public void eightComponents() {

driver.manage().timeouts().implicitlyWait(Duration.ofMillis(500));

driver.get("https://www.selenium.dev/selenium/web/web-form.html");

String title = driver.getTitle();

assertEquals("Web form", title);

WebElement textBox = driver.findElement(By.name("my-text"));

WebElement submitButton = driver.findElement(By.cssSelector("button"));

textBox.sendKeys("Selenium");

submitButton.click();

WebElement message = driver.findElement(By.id("message"));

String value = message.getText();

assertEquals("Received!", value);

}

@AfterEach

public void teardown() {

driver.quit();

}

}

Tear Down @AfterEach

public void teardown() {

driver.quit();

}View Complete Code

View on GitHub/examples/java/src/test/java/dev/selenium/getting_started/UsingSeleniumTest.java

Copy

Closepackage dev.selenium.getting_started;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.time.Duration;

import org.junit.jupiter.api.AfterEach;

import org.junit.jupiter.api.BeforeEach;

import org.junit.jupiter.api.Test;

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.chrome.ChromeDriver;

public class UsingSeleniumTest {

WebDriver driver;

@BeforeEach

public void setup() {

driver = new ChromeDriver();

}

@Test

public void eightComponents() {

driver.manage().timeouts().implicitlyWait(Duration.ofMillis(500));

driver.get("https://www.selenium.dev/selenium/web/web-form.html");

String title = driver.getTitle();

assertEquals("Web form", title);

WebElement textBox = driver.findElement(By.name("my-text"));

WebElement submitButton = driver.findElement(By.cssSelector("button"));

textBox.sendKeys("Selenium");

submitButton.click();

WebElement message = driver.findElement(By.id("message"));

String value = message.getText();

assertEquals("Received!", value);

}

@AfterEach

public void teardown() {

driver.quit();

}

}

Set Updef setup():

driver = webdriver.Chrome()

driver.get("https://www.selenium.dev/selenium/web/web-form.html")

return driverView Complete Code

View on GitHub/examples/python/tests/getting_started/using_selenium_tests.py

Copy

Closefrom selenium import webdriver

from selenium.webdriver.common.by import By

def test_eight_components():

driver = setup()

title = driver.title

assert title == "Web form"

driver.implicitly_wait(0.5)

text_box = driver.find_element(by=By.NAME, value="my-text")

submit_button = driver.find_element(by=By.CSS_SELECTOR, value="button")

text_box.send_keys("Selenium")

submit_button.click()

message = driver.find_element(by=By.ID, value="message")

value = message.text

assert value == "Received!"

teardown(driver)

def setup():

driver = webdriver.Chrome()

driver.get("https://www.selenium.dev/selenium/web/web-form.html")

return driver

def teardown(driver):

driver.quit()

Tear Downdef teardown(driver):

driver.quit()View Complete Code

View on GitHub/examples/python/tests/getting_started/using_selenium_tests.py

Copy

Closefrom selenium import webdriver

from selenium.webdriver.common.by import By

def test_eight_components():

driver = setup()

title = driver.title

assert title == "Web form"

driver.implicitly_wait(0.5)

text_box = driver.find_element(by=By.NAME, value="my-text")

submit_button = driver.find_element(by=By.CSS_SELECTOR, value="button")

text_box.send_keys("Selenium")

submit_button.click()

message = driver.find_element(by=By.ID, value="message")

value = message.text

assert value == "Received!"

teardown(driver)

def setup():

driver = webdriver.Chrome()

driver.get("https://www.selenium.dev/selenium/web/web-form.html")

return driver

def teardown(driver):

driver.quit()

Add Example

Set Up before do

@driver = Selenium::WebDriver.for :chrome

endView Complete Code

View on GitHub/examples/ruby/spec/getting_started/using_selenium_spec.rb

Copy

Close# frozen_string_literal: true

require 'spec_helper'

require 'selenium-webdriver'

RSpec.describe 'Using Selenium' do

before do

@driver = Selenium::WebDriver.for :chrome

end

it 'uses eight components' do

@driver.get('https://www.selenium.dev/selenium/web/web-form.html')

title = @driver.title

expect(title).to eq('Web form')

@driver.manage.timeouts.implicit_wait = 500

text_box = @driver.find_element(name: 'my-text')

submit_button = @driver.find_element(tag_name: 'button')

text_box.send_keys('Selenium')

submit_button.click

message = @driver.find_element(id: 'message')

value = message.text

expect(value).to eq('Received!')

end

end

Tear Down config.after { @driver&.quit }View Complete Code

View on GitHub/examples/ruby/spec/spec_helper.rb

Copy

Close# frozen_string_literal: true

require 'selenium-webdriver'

require 'selenium/webdriver/support/guards'

RSpec.configure do |config|

# Enable flags like --only-failures and --next-failure

config.example_status_persistence_file_path = '.rspec_status'

# Disable RSpec exposing methods globally on `Module` and `main`

config.disable_monkey_patching!

Dir.mktmpdir('tmp')

config.example_status_persistence_file_path = 'tmp/examples.txt'

config.expect_with :rspec do |c|

c.syntax = :expect

end

config.before do |example|

bug_tracker = 'https://github.com/SeleniumHQ/seleniumhq.github.io/issues'

guards = Selenium::WebDriver::Support::Guards.new(example,

bug_tracker: bug_tracker)

guards.add_condition(:platform, Selenium::WebDriver::Platform.os)

guards.add_condition(:ci, Selenium::WebDriver::Platform.ci)

results = guards.disposition

send(*results) if results

end

config.after { @driver&.quit }

def start_session

options = Selenium::WebDriver::Chrome::Options.new

options.add_argument('disable-search-engine-choice-screen')

options.add_argument('--no-sandbox')

@driver = Selenium::WebDriver.for(:chrome, options: options)

end

def start_bidi_session

options = Selenium::WebDriver::Chrome::Options.new(web_socket_url: true)

@driver = Selenium::WebDriver.for :chrome, options: options

end

def start_firefox

options = Selenium::WebDriver::Options.firefox(timeouts: {implicit: 1500})

@driver = Selenium::WebDriver.for :firefox, options: options

end

end

### Set Up before(async function () {

driver = await new Builder().forBrowser('chrome').build();

});View Complete Code

View on GitHub/examples/javascript/test/getting_started/runningTests.spec.js

Copy

Closeconst {By, Builder} = require('selenium-webdriver');

const assert = require("assert");

describe('First script', function () {

let driver;

before(async function () {

driver = await new Builder().forBrowser('chrome').build();

});

it('First Selenium script with mocha', async function () {

await driver.get('https://www.selenium.dev/selenium/web/web-form.html');

let title = await driver.getTitle();

assert.equal("Web form", title);

await driver.manage().setTimeouts({implicit: 500});

let textBox = await driver.findElement(By.name('my-text'));

let submitButton = await driver.findElement(By.css('button'));

await textBox.sendKeys('Selenium');

await submitButton.click();

let message = await driver.findElement(By.id('message'));

let value = await message.getText();

assert.equal("Received!", value);

});

after(async () => await driver.quit());

});### Tear Down after(async () => await driver.quit());View Complete Code

View on GitHub/examples/javascript/test/getting_started/runningTests.spec.js

Copy

Closeconst {By, Builder} = require('selenium-webdriver');

const assert = require("assert");

describe('First script', function () {

let driver;

before(async function () {

driver = await new Builder().forBrowser('chrome').build();

});

it('First Selenium script with mocha', async function () {

await driver.get('https://www.selenium.dev/selenium/web/web-form.html');

let title = await driver.getTitle();

assert.equal("Web form", title);

await driver.manage().setTimeouts({implicit: 500});

let textBox = await driver.findElement(By.name('my-text'));

let submitButton = await driver.findElement(By.css('button'));

await textBox.sendKeys('Selenium');

await submitButton.click();

let message = await driver.findElement(By.id('message'));

let value = await message.getText();

assert.equal("Received!", value);

});

after(async () => await driver.quit());

});Add Example

执行

Java

Python

CSharp

Ruby

JavaScript

KotlinMavenmvn clean test

Gradlegradle clean test

```View Complete Code

View on GitHub/examples/python/README.md

Copy

Close# Running tests from Selenium Python examples

#### 1. Clone this repository

```

git clone https://github.com/SeleniumHQ/seleniumhq.github.io.git

```

#### 2. Navigate to `python` directory

```

cd seleniumhq.github.io/examples/python

```

#### 3. Create a virtual environment

- On Windows:

```

py -m venv venv

venv\Scripts\activate

```

- On Linux/Mac:

```

python3 -m venv venv

source venv/bin/activate

```

#### 4. Install dependencies:

```

pip install -r requirements.txt

```

> for help, see: https://packaging.python.org/en/latest/tutorials/installing-packages

#### 5. Run tests

- Run all tests with the default Python interpreter:

```

pytest

```

- Run all tests with every installed/supported Python interpreter:

```

tox

```

> Please have some patience - If you are doing it for the first time, it will take a little while to download the browser drivers

- Run a specific example:

```

pytest path/to/test_script.py

```

> Make sure to replace `path/to/test_script.py` with the path and name of the example you want to run

Add Example

bundle exec rspecView Complete Code

View on GitHub/examples/ruby/README.md

Copy

Close# Running all tests from Selenium ruby example

Follow these steps to run all test example from selenium ruby

1. Clone this repository

```

git clone https://github.com/SeleniumHQ/seleniumhq.github.io.git

```

2. Navigate to `ruby` directory

```

cd seleniumhq.github.io/examples/ruby

```

3. Install dependencies using bundler

```

bundler install

```

4. Run all tests

```

bundle exec rspec

```

> Please keep some patience - If you are doing it for the first time, it will take a little while to verify and download the browser drivers

# Execute a ruby script

Use this command to run a ruby script and follow the first script example

```

ruby example_script.rb

```Mochamocha runningTests.spec.js

npxnpx mocha runningTests.spec.js

Add Example

示例在第一个脚本一节中,我们了解了 Selenium 脚本的每一个组件。

这里是使用 test runner 重新组织那个脚本的一个示例:

Java

Python

CSharp

Ruby

JavaScript

Kotlinpackage dev.selenium.getting_started;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.time.Duration;

import org.junit.jupiter.api.AfterEach;

import org.junit.jupiter.api.BeforeEach;

import org.junit.jupiter.api.Test;

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.chrome.ChromeDriver;

public class UsingSeleniumTest {

WebDriver driver;

@BeforeEach

public void setup() {

driver = new ChromeDriver();

}

@Test

public void eightComponents() {

driver.manage().timeouts().implicitlyWait(Duration.ofMillis(500));

driver.get("https://www.selenium.dev/selenium/web/web-form.html");

String title = driver.getTitle();

assertEquals("Web form", title);

WebElement textBox = driver.findElement(By.name("my-text"));

WebElement submitButton = driver.findElement(By.cssSelector("button"));

textBox.sendKeys("Selenium");

submitButton.click();

WebElement message = driver.findElement(By.id("message"));

String value = message.getText();

assertEquals("Received!", value);

}

@AfterEach

public void teardown() {

driver.quit();

}

}

View on GitHubfrom selenium import webdriver

from selenium.webdriver.common.by import By

def test_eight_components():

driver = setup()

title = driver.title

assert title == "Web form"

driver.implicitly_wait(0.5)

text_box = driver.find_element(by=By.NAME, value="my-text")

submit_button = driver.find_element(by=By.CSS_SELECTOR, value="button")

text_box.send_keys("Selenium")

submit_button.click()

message = driver.find_element(by=By.ID, value="message")

value = message.text

assert value == "Received!"

teardown(driver)

def setup():

driver = webdriver.Chrome()

driver.get("https://www.selenium.dev/selenium/web/web-form.html")

return driver

def teardown(driver):

driver.quit()

View on GitHubusing System;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using OpenQA.Selenium;

using OpenQA.Selenium.Chrome;

namespace SeleniumDocs.GettingStarted

{

[TestClass]

public class UsingSeleniumTest

{

[TestMethod]

public void EightComponents()

{

IWebDriver driver = new ChromeDriver();

driver.Navigate().GoToUrl("https://www.selenium.dev/selenium/web/web-form.html");

var title = driver.Title;

Assert.AreEqual("Web form", title);

driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(500);

var textBox = driver.FindElement(By.Name("my-text"));

var submitButton = driver.FindElement(By.TagName("button"));

textBox.SendKeys("Selenium");

submitButton.Click();

var message = driver.FindElement(By.Id("message"));

var value = message.Text;

Assert.AreEqual("Received!", value);

driver.Quit();

}

}

}

View on GitHub# frozen_string_literal: true

require 'spec_helper'

require 'selenium-webdriver'

RSpec.describe 'Using Selenium' do

before do

@driver = Selenium::WebDriver.for :chrome

end

it 'uses eight components' do

@driver.get('https://www.selenium.dev/selenium/web/web-form.html')

title = @driver.title

expect(title).to eq('Web form')

@driver.manage.timeouts.implicit_wait = 500

text_box = @driver.find_element(name: 'my-text')

submit_button = @driver.find_element(tag_name: 'button')

text_box.send_keys('Selenium')

submit_button.click

message = @driver.find_element(id: 'message')

value = message.text

expect(value).to eq('Received!')

end

end

View on GitHubconst {By, Builder} = require('selenium-webdriver');

const assert = require("assert");

describe('First script', function () {

let driver;

before(async function () {

driver = await new Builder().forBrowser('chrome').build();

});

it('First Selenium script with mocha', async function () {

await driver.get('https://www.selenium.dev/selenium/web/web-form.html');

let title = await driver.getTitle();

assert.equal("Web form", title);

await driver.manage().setTimeouts({implicit: 500});

let textBox = await driver.findElement(By.name('my-text'));

let submitButton = await driver.findElement(By.css('button'));

await textBox.sendKeys('Selenium');

await submitButton.click();

let message = await driver.findElement(By.id('message'));

let value = await message.getText();

assert.equal("Received!", value);

});

after(async () => await driver.quit());

});

View on GitHubAdd Example

下一步使用你目前所学到的知识构建你自己的 Selenium 代码吧!

想要了解更多的功能特性,

请继续阅读我们接下来的WebDriver 教程

最后修改 July 29, 2025: Adding trailing / to retrieve code from GitHub (48b97e9bf82)

🔮 相关作品

良心的意思
哪个才是365官网

良心的意思

📅 08-29 👁️‍🗨️ 6255
【烟台大鱿鱼】鱿鱼干怎么保存
bt365博彩

【烟台大鱿鱼】鱿鱼干怎么保存

📅 07-07 👁️‍🗨️ 1566