892f39401c
- Add NewSessionScenario that walks the Telegram wizard: single game, title, skip description/cover, D&D 5e, 4h, datetime, capacity, online format, join link, public visibility, publish, confirm - Add ClickInlineButtonAsync / ClickInlineButtonByTextAsync to TelegramUserClient - Add local WizardCallback/Step constants mirroring GmRelay.Shared wizard wire format - Program.cs now runs full flow: group setup + /newsession + cleanup Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
133 lines
5.2 KiB
C#
133 lines
5.2 KiB
C#
namespace GmRelay.E2E.Runner;
|
|
|
|
/// <summary>
|
|
/// E2E scenario that walks through the GmRelay /newsession wizard in a Telegram group
|
|
/// and verifies that a session is created and visible in the Web dashboard.
|
|
/// </summary>
|
|
public sealed class NewSessionScenario
|
|
{
|
|
private readonly TelegramUserClient _client;
|
|
private readonly RunnerConfig _config;
|
|
|
|
public NewSessionScenario(TelegramUserClient client, RunnerConfig config)
|
|
{
|
|
_client = client;
|
|
_config = config;
|
|
}
|
|
|
|
public async Task<ScenarioResult> RunAsync(
|
|
ChatGroup group,
|
|
NewSessionInputs inputs,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
await _client.SendCommandAsync(group, "newsession", cancellationToken);
|
|
await WaitForStepAsync(group, WizardStep.Type, cancellationToken);
|
|
|
|
await _client.ClickInlineButtonAsync(
|
|
group,
|
|
WizardCallback.Choice(WizardStep.Type, "single"),
|
|
cancellationToken: cancellationToken);
|
|
await WaitForStepAsync(group, WizardStep.Title, cancellationToken);
|
|
|
|
await _client.SendMessageAsync(group, inputs.Title, cancellationToken);
|
|
await WaitForStepAsync(group, WizardStep.Description, cancellationToken);
|
|
|
|
await _client.SendMessageAsync(group, "-", cancellationToken);
|
|
await WaitForStepAsync(group, WizardStep.Cover, cancellationToken);
|
|
|
|
await _client.SendMessageAsync(group, "-", cancellationToken);
|
|
await WaitForStepAsync(group, WizardStep.System, cancellationToken);
|
|
|
|
await _client.ClickInlineButtonAsync(
|
|
group,
|
|
WizardCallback.Choice(WizardStep.System, "Dnd5e"),
|
|
cancellationToken: cancellationToken);
|
|
await WaitForStepAsync(group, WizardStep.Duration, cancellationToken);
|
|
|
|
await _client.ClickInlineButtonAsync(
|
|
group,
|
|
WizardCallback.Choice(WizardStep.Duration, "240"),
|
|
cancellationToken: cancellationToken);
|
|
await WaitForStepAsync(group, WizardStep.DateTime, cancellationToken);
|
|
|
|
await _client.SendMessageAsync(group, inputs.ScheduledAtMoscow, cancellationToken);
|
|
await WaitForStepAsync(group, WizardStep.Capacity, cancellationToken);
|
|
|
|
await _client.SendMessageAsync(group, inputs.MaxPlayers.ToString(System.Globalization.CultureInfo.InvariantCulture), cancellationToken);
|
|
await WaitForStepAsync(group, WizardStep.Format, cancellationToken);
|
|
|
|
await _client.ClickInlineButtonAsync(
|
|
group,
|
|
WizardCallback.Choice(WizardStep.Format, "online"),
|
|
cancellationToken: cancellationToken);
|
|
await WaitForStepAsync(group, WizardStep.Location, cancellationToken);
|
|
|
|
await _client.SendMessageAsync(group, inputs.JoinLink, cancellationToken);
|
|
await WaitForStepAsync(group, WizardStep.Visibility, cancellationToken);
|
|
|
|
await _client.ClickInlineButtonAsync(
|
|
group,
|
|
WizardCallback.Choice(WizardStep.Visibility, "public"),
|
|
cancellationToken: cancellationToken);
|
|
await WaitForStepAsync(group, WizardStep.Publish, cancellationToken);
|
|
|
|
await _client.ClickInlineButtonAsync(
|
|
group,
|
|
WizardCallback.Choice(WizardStep.Publish, "yes"),
|
|
cancellationToken: cancellationToken);
|
|
await WaitForStepAsync(group, WizardStep.Confirm, cancellationToken);
|
|
|
|
await _client.ClickInlineButtonAsync(
|
|
group,
|
|
WizardCallback.Create(),
|
|
cancellationToken: cancellationToken);
|
|
|
|
var confirmation = await _client.WaitForBotReplyAsync(
|
|
group,
|
|
containsText: "Создано",
|
|
timeout: TimeSpan.FromSeconds(60),
|
|
cancellationToken);
|
|
|
|
if (confirmation is null)
|
|
throw new InvalidOperationException("Wizard did not confirm session creation.");
|
|
|
|
Console.WriteLine($"[scenario] session created (msg id={confirmation.id})");
|
|
return new ScenarioResult(group, confirmation.id);
|
|
}
|
|
|
|
private async Task WaitForStepAsync(
|
|
ChatGroup group,
|
|
string expectedStep,
|
|
CancellationToken cancellationToken = default,
|
|
TimeSpan? timeout = null)
|
|
{
|
|
var deadline = DateTime.UtcNow + (timeout ?? TimeSpan.FromSeconds(30));
|
|
while (DateTime.UtcNow < deadline)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
var message = await _client.GetLatestBotMessageAsync(group, cancellationToken);
|
|
if (message?.reply_markup is TL.ReplyInlineMarkup markup)
|
|
{
|
|
var dataButtons = markup.rows
|
|
.SelectMany(r => r.buttons)
|
|
.OfType<TL.KeyboardButtonCallback>()
|
|
.Select(b => System.Text.Encoding.UTF8.GetString(b.data))
|
|
.ToList();
|
|
|
|
if (dataButtons.Any(d => d.StartsWith($"wizard:{expectedStep}", StringComparison.Ordinal)))
|
|
return;
|
|
}
|
|
|
|
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
|
|
}
|
|
|
|
throw new TimeoutException($"Wizard did not reach step '{expectedStep}' in time.");
|
|
}
|
|
}
|
|
|
|
public sealed record NewSessionInputs(
|
|
string Title,
|
|
string ScheduledAtMoscow,
|
|
int MaxPlayers,
|
|
string JoinLink);
|