OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)

This commit is contained in:
wangruiguo
2026-09-03 18:49:20 +08:00
commit d647428529
3501 changed files with 1988906 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace Tests\Support;
use Facebook\WebDriver\Exception\ElementClickInterceptedException;
use Facebook\WebDriver\Exception\StaleElementReferenceException;
/**
* Inherited Methods
*
* @method void wantTo($text)
* @method void wantToTest($text)
* @method void execute($callable)
*/
class AcceptanceTester extends \Codeception\Actor
{
use _generated\AcceptanceTesterActions;
/*
* Click on an element with retries and waits
*/
public function clickWithRetry($selector, $timeout = 10)
{
$maxAttempts = 3;
$attempt = 1;
while ($attempt <= $maxAttempts) {
try {
// Wait for element to be clickable
$this->waitForElementClickable($selector, $timeout);
// Scroll element into view
$this->executeJS("document.querySelector('".$selector."').scrollIntoView({behavior: 'smooth', block: 'center'});");
$this->wait(1); // Small wait after scroll
// Try to click
$this->click($selector);
return;
} catch (ElementClickInterceptedException|StaleElementReferenceException $e) {
if ($attempt === $maxAttempts) {
throw $e;
}
$this->wait(1);
$attempt++;
}
}
}
/**
* Take debug screenshot on failure
*/
public function _failed(\Codeception\TestInterface $test, $fail)
{
$this->makeScreenshot('failed_'.$test->getName());
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Tests\Support;
/**
* Inherited Methods
*
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method void pause()
*/
class ApiTester extends \Codeception\Actor
{
use _generated\ApiTesterActions;
}

View File

View File

@@ -0,0 +1,36 @@
<?php
namespace Tests\Support\Helper;
use Codeception\Module;
use Illuminate\Support\Facades\Session;
use Leantime\Core\Application;
class Acceptance extends Module
{
protected Application $app;
public function _initialize()
{
$this->app = require dirname(__DIR__, 2).'/bootstrap.php';
if (! defined('BASE_URL')) {
define('BASE_URL', 'https://leantime-dev');
}
}
public function getApplication(): Application
{
return $this->app;
}
public function setCSRFToken()
{
$token = bin2hex(random_bytes(32));
Session::put('_token', $token);
Session::save();
// Set the token in the cookie as well
$this->getModule('WebDriver')->setCookie('XSRF-TOKEN', $token);
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Tests\Support\Helper;
use Codeception\Module;
use Leantime\Core\Application;
class Api extends Module
{
protected Application $app;
public function _initialize()
{
$this->app = require dirname(__DIR__, 2).'/bootstrap.php';
}
public function getApplication(): Application
{
return $this->app;
}
public function haveHttpHeader($header, $value)
{
$this->getModule('REST')->haveHttpHeader($header, $value);
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Tests\Support\Helper;
use Codeception\Module;
use Leantime\Core\Application;
class Unit extends Module
{
/**
* @var Application
*/
protected $app;
public function _initialize()
{
$this->app = bootstrap_minimal_app();
}
public function getApplication()
{
return $this->app;
}
public function haveInDatabase($table, array $data)
{
return $this->getModule('Db')->haveInDatabase($table, $data);
}
public function seeInDatabase($table, array $criteria)
{
return $this->getModule('Db')->seeInDatabase($table, $criteria);
}
public function dontSeeInDatabase($table, array $criteria)
{
return $this->getModule('Db')->dontSeeInDatabase($table, $criteria);
}
}

View File

@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
namespace Tests\Support\Page\Acceptance;
use Codeception\Util\Fixtures;
use Tests\Support\AcceptanceTester;
class Install
{
protected AcceptanceTester $I;
protected $app;
public function __construct(AcceptanceTester $I)
{
$this->I = $I;
$this->app = $I->getApplication();
}
public function install($email, $password, $firstname, $lastname, $company): void
{
if (Fixtures::exists('installed')) {
$this->suppressModals();
return;
}
$this->I->amOnPage('/install');
$this->I->fillField(['name' => 'email'], $email);
$this->I->fillField(['name' => 'firstname'], $firstname);
$this->I->fillField(['name' => 'lastname'], $lastname);
$this->I->fillField(['name' => 'company'], $company);
$this->I->click('Install');
// A successful install redirects straight into the onboarding wizard
// (auth/userInvite), starting on the "Setting Account Details" step —
// there is no longer a standalone success .alert page. Wait for the
// jobTitle field (the first field this method fills) to confirm the
// step has rendered before continuing.
$this->I->waitForElementVisible(['name' => 'jobTitle'], 90);
$this->I->fillField(['name' => 'jobTitle'], 'CEO');
$this->I->fillField(['name' => 'password'], $password);
$this->I->click('Next');
$this->I->waitForText('Determining A Visual Experience', 90);
$this->I->click('Next');
$this->I->waitForText('Creating A Comfortable View', 90);
$this->I->click('Next');
$this->I->waitForText('Shaping A Daily Flow', 90);
$this->I->click('Next');
$this->I->waitForText('Your Leantime journey is about to begin', 90);
$this->I->click('Complete Sign up');
Fixtures::add('installed', true);
$this->suppressModals();
}
/**
* Suppress all helper modals for testing
*/
private function suppressModals(): void
{
$userService = $this->app->make(\Leantime\Domain\Users\Services\Users::class);
session(['userdata.id' => 1]);
// Suppress all known modals
$userService->updateUserSettings('modals', 'projectDashboard', true);
$userService->updateUserSettings('modals', 'home', true);
$userService->updateUserSettings('modals', 'kanban', true);
$userService->updateUserSettings('modals', 'roadmap', true);
$userService->updateUserSettings('modals', 'goals', true);
}
}

View File

@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace Tests\Support\Page\Acceptance;
use Codeception\Util\Fixtures;
class Login
{
/**
* @var \Tests\Support\AcceptanceTester;
*/
protected $I;
public function __construct(\Tests\Support\AcceptanceTester $I, Install $installPage)
{
$this->I = $I;
$this->installPage = $installPage;
}
public function login($username, $password)
{
if ($this->loadSessionShapshot('leantime_session')) {
return;
}
if (! Fixtures::exists('installed')) {
$this->installPage->install(
'test@leantime.io',
'Test123456!',
'John',
'Smith',
'Smith & Co'
);
}
$this->I->amOnPage('/auth/login');
$this->I->fillField(['name' => 'username'], $username);
$this->I->fillField(['name' => 'password'], $password);
$this->I->click('Login');
$this->I->waitForElementVisible('.welcome-widget', 120);
$this->I->see('Hi John');
$this->saveSessionSnapshot('leantime_session');
}
protected function loadSessionShapshot(string $name): bool
{
if (! Fixtures::exists($name)) {
return false;
}
$this->I->setCookie($name, Fixtures::get($name));
return true;
}
protected function saveSessionSnapshot(string $name): void
{
Fixtures::add($name, $this->I->grabCookie($name));
}
}

View File

@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Tests\Support;
/**
* Inherited Methods
*
* @method void wantTo($text)
* @method void wantToTest($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method void pause($vars = [])
*
* @SuppressWarnings(PHPMD)
*/
class UnitTester extends \Codeception\Actor
{
use _generated\UnitTesterActions;
/**
* Define custom actions here
*/
}

2
tests/Support/_generated/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore