Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions shortcuts/slides/slides_screenshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
}
}
if len(slideIDs) == 0 && len(slideNumbers) == 0 {
return slidesScreenshotFlagErrorf("--slide-id or --slide-number is required")
return slidesScreenshotMissingSelectorError()
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
if err := validateSlidesScreenshotSelectorLimit(len(slideIDs) + len(slideNumbers)); err != nil {
return err
Expand All @@ -104,9 +104,6 @@
if err != nil {
return common.NewDryRunAPI().Set("error", err.Error())
}
if len(slideIDs) == 0 && len(slideNumbers) == 0 {
return common.NewDryRunAPI().Set("error", "--slide-id or --slide-number is required")
}
if err := validateSlidesScreenshotSelectorLimit(len(slideIDs) + len(slideNumbers)); err != nil {
return common.NewDryRunAPI().Set("error", err.Error())
}
Expand Down Expand Up @@ -154,7 +151,7 @@
return err
}
if len(slideIDs) == 0 && len(slideNumbers) == 0 {
return slidesScreenshotFlagErrorf("--slide-id or --slide-number is required")
return slidesScreenshotMissingSelectorError()

Check warning on line 154 in shortcuts/slides/slides_screenshot.go

View check run for this annotation

Codecov / codecov/patch

shortcuts/slides/slides_screenshot.go#L154

Added line #L154 was not covered by tests
}
if err := validateSlidesScreenshotSelectorLimit(len(slideIDs) + len(slideNumbers)); err != nil {
return err
Expand Down Expand Up @@ -264,6 +261,9 @@
}

slideIDValues := append([]string(nil), runtime.StrSlice("slide-id")...)
if runtime.Changed("slide-id") && len(normalizeSlideIDs(slideIDValues)) == 0 {
return nil, nil, slidesScreenshotEmptySlideIDError()
}
if aliasSlideIsID {
slideIDValues = append(slideIDValues, aliasSlide)
}
Expand Down Expand Up @@ -375,6 +375,17 @@
return nil
}

func slidesScreenshotMissingSelectorError() error {
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--slide-id or --slide-number is required").
WithHint("specify up to 10 slides with --slide-id <slide_id> or --slide-number <number>; repeat the flag or use comma-separated values for multiple slides")
}

func slidesScreenshotEmptySlideIDError() error {
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--slide-id cannot be empty").
WithParam("--slide-id").
WithHint("provide a non-empty slide ID or use --slide-number <number>")
}

func slidesScreenshotFlagErrorf(format string, args ...interface{}) error {
return errs.NewValidationError(errs.SubtypeInvalidArgument, format, args...)
}
Expand Down
70 changes: 59 additions & 11 deletions shortcuts/slides/slides_screenshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -670,18 +670,66 @@ func TestSlidesScreenshotAvoidsOverwritingExistingFile(t *testing.T) {
}

func TestSlidesScreenshotListRequiresSelector(t *testing.T) {
f, stdout, _, _ := cmdutil.TestFactory(t, slidesTestConfig(t, ""))

err := runSlidesShortcut(t, f, stdout, SlidesScreenshot, []string{
"+screenshot",
"--presentation", "pres_abc",
"--as", "user",
})
if err == nil {
t.Fatal("expected error")
tests := []struct {
name string
args []string
wantMessage string
wantHint string
wantParam string
}{
{
name: "omitted",
args: nil,
wantMessage: "--slide-id or --slide-number is required",
wantHint: "specify up to 10 slides with --slide-id <slide_id> or --slide-number <number>; repeat the flag or use comma-separated values for multiple slides",
},
{
name: "empty slide ID",
args: []string{"--slide-id", ""},
wantMessage: "--slide-id cannot be empty",
wantHint: "provide a non-empty slide ID or use --slide-number <number>",
wantParam: "--slide-id",
},
{
name: "empty slide ID with slide number",
args: []string{"--slide-id", "", "--slide-number", "1"},
wantMessage: "--slide-id cannot be empty",
wantHint: "provide a non-empty slide ID or use --slide-number <number>",
wantParam: "--slide-id",
},
}
if !strings.Contains(err.Error(), "--slide-id or --slide-number is required") {
t.Fatalf("error = %v, want missing selector error", err)

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f, stdout, _, _ := cmdutil.TestFactory(t, slidesTestConfig(t, ""))
args := append([]string{"+screenshot", "--presentation", "pres_abc"}, tt.args...)
args = append(args, "--as", "user")

err := runSlidesShortcut(t, f, stdout, SlidesScreenshot, args)
if err == nil {
t.Fatal("expected error")
}
problem, ok := errs.ProblemOf(err)
if !ok {
t.Fatalf("error = %T %v, want typed validation error", err, err)
}
if problem.Category != errs.CategoryValidation || problem.Subtype != errs.SubtypeInvalidArgument {
t.Fatalf("problem = %#v, want validation/invalid_argument", problem)
}
if problem.Message != tt.wantMessage {
t.Fatalf("message = %q, want %q", problem.Message, tt.wantMessage)
}
if problem.Hint != tt.wantHint {
t.Fatalf("hint = %q, want %q", problem.Hint, tt.wantHint)
}
var validationErr *errs.ValidationError
if !errors.As(err, &validationErr) {
t.Fatalf("error type = %T, want *errs.ValidationError", err)
}
if validationErr.Param != tt.wantParam {
t.Fatalf("param = %q, want %q", validationErr.Param, tt.wantParam)
}
})
}
}

Expand Down
2 changes: 1 addition & 1 deletion skills/lark-slides/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ metadata:
| 删除页面 | 按 `slide_id` 单页删除,删前先回读确认 | `slides +delete-slide`、`lark-slides-delete-slide.md` |
| 读取或分析已有 PPT | 解析 slides/wiki token,用 shortcut 回读全文 XML 或读取单页 XML,保存 `xml_presentation_id`、`slide_id`、`revision_id` | `slides +xml-get`、`xml_presentation.slide.get`、`lark-slides-xml-presentations-get.md` |
| 查看或回滚历史版本 | 先用 `+history-list` 找 `history_version_id`,再 `+history-revert`,必要时 `+history-revert-status` 轮询 | [`lark-slides-history.md`](references/lark-slides-history.md) |
| 获取幻灯片页面截图 | 用 `slide_id` 或页号指定页面,一次不超过 10 | `slides +screenshot`、`lark-slides-screenshot.md` |
| 获取幻灯片页面截图 | 指定页面直接截图;全量截图枚举全部页面 ID 或页码,再按每批最多 10 页串行执行 | `slides +screenshot`、`lark-slides-screenshot.md` |
| 上传或使用图片 | 先上传为 `file_token`,禁止直接写 http(s) 外链 | `slides +media-upload`、`lark-slides-media-upload.md`,或 `+create --slides` 的 XML 里写 `<img src="@./path">` 占位符 |
| 绘制图表 | 原生图表(柱状、条形、折线、面积、饼(环)、雷达、组合图)用 `<chart>`,其他(漏斗图、金字塔图、象限图、矩阵图等)用 `<shape>` + `<line>` 模拟 | `xml-schema-quick-ref.md`、`slides_chart_demo.xml` |
| 绘制表格 | 优先用 `rect` 和 `text` 模拟,其他用 `<table>` | `xml-schema-quick-ref.md` |
Expand Down
4 changes: 4 additions & 0 deletions skills/lark-slides/references/lark-slides-screenshot.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ lark-cli slides +screenshot --as user \
--content @slide.xml
```

## 截图全部页面

枚举全部页面的 `slide_id` 或页码,按每批最多 10 页分组并串行调用 `slides +screenshot`,复用同一个 `--output-dir`;记录失败批次,已完成批次不重复执行。

## 参数

| 参数 | 必需 | 说明 |
Expand Down
48 changes: 48 additions & 0 deletions tests/cli_e2e/slides/slides_screenshot_dryrun_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,54 @@ func TestSlidesScreenshotSlideIDCSVDryRunE2E(t *testing.T) {
require.Equal(t, "slide_2", slideIDs[1].String(), result.Stdout)
}

func TestSlidesScreenshotRequiresSelectorDryRunE2E(t *testing.T) {
setSlidesDryRunEnv(t)

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
t.Cleanup(cancel)

result, err := clie2e.RunCmd(ctx, clie2e.Request{
Args: []string{
"slides", "+screenshot",
"--presentation", "presScreenshotMissingSelector",
"--dry-run",
},
DefaultAs: "bot",
})
require.NoError(t, err)
result.AssertExitCode(t, 2)
require.Equal(t, "validation", gjson.Get(result.Stderr, "error.type").String(), result.Stderr)
require.Equal(t, "invalid_argument", gjson.Get(result.Stderr, "error.subtype").String(), result.Stderr)
require.Equal(t, "--slide-id or --slide-number is required", gjson.Get(result.Stderr, "error.message").String(), result.Stderr)
require.Contains(t, gjson.Get(result.Stderr, "error.hint").String(), "--slide-id <slide_id>", result.Stderr)
require.Empty(t, result.Stdout)
}

func TestSlidesScreenshotRejectsEmptySlideIDWithSlideNumberDryRunE2E(t *testing.T) {
setSlidesDryRunEnv(t)

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
t.Cleanup(cancel)

result, err := clie2e.RunCmd(ctx, clie2e.Request{
Args: []string{
"slides", "+screenshot",
"--presentation", "presScreenshotEmptyID",
"--slide-id", "",
"--slide-number", "1",
"--dry-run",
},
DefaultAs: "bot",
})
require.NoError(t, err)
result.AssertExitCode(t, 2)
require.Equal(t, "validation", gjson.Get(result.Stderr, "error.type").String(), result.Stderr)
require.Equal(t, "invalid_argument", gjson.Get(result.Stderr, "error.subtype").String(), result.Stderr)
require.Equal(t, "--slide-id", gjson.Get(result.Stderr, "error.param").String(), result.Stderr)
require.Equal(t, "--slide-id cannot be empty", gjson.Get(result.Stderr, "error.message").String(), result.Stderr)
require.Empty(t, result.Stdout)
}

func TestSlidesScreenshotAliasesDryRunE2E(t *testing.T) {
setSlidesDryRunEnv(t)

Expand Down
Loading