xlsx/modify-add-row

Open cell-bold.xlsx (1 row: A1='Hello bold cell' bold), ws.append([1, 2, 3]), save. Assert (a) the existing row 1 is preserved intact (A1 keeps @s='1' and its shared-string reference), (b) a new row r='2' with cells A2,B2,C2 carrying numeric values 1,2,3 is present, (c) dimension ref expanded to A1:C2.

Library verdicts: python-xlsx: — xlsxjs: —

Metadata

Feature idxlsx/modify-add-row
Formatxlsx
Categorymodify-in-place
Spececma-376-5-part-1 § 18.3.1.73 row

XPath assertions

ID / partPredicateXPathpython-xlsxxlsxjs
row-count-is-two
xl/worksheets/sheet1.xml
match = ^2(\.0+)?$
sheetData must contain exactly 2 rows.
count(//s:sheetData/s:row)
original-a1-style-preserved
xl/worksheets/sheet1.xml
equal = 1
Row 1's A1 must keep its bold style index. Append must not re-author pre-existing rows.
//s:sheetData/s:row[@r='1']/s:c[@r='A1']/@s
original-a1-text-preserved
xl/sharedStrings.xml
exist
The original shared string must still be present — append of numeric values must not touch the string table.
//s:si/s:t[normalize-space()='Hello bold cell']
row2-a2-value
xl/worksheets/sheet1.xml
equal = 1
A2 must carry the first appended value (1).
//s:sheetData/s:row[@r='2']/s:c[@r='A2']/s:v
row2-b2-value
xl/worksheets/sheet1.xml
equal = 2
B2 must carry the second appended value (2).
//s:sheetData/s:row[@r='2']/s:c[@r='B2']/s:v
row2-c2-value
xl/worksheets/sheet1.xml
equal = 3
C2 must carry the third appended value (3).
//s:sheetData/s:row[@r='2']/s:c[@r='C2']/s:v
row2-cells-are-numeric
xl/worksheets/sheet1.xml
not-match = ^s$
A2 must NOT be shared-string-typed — numeric append must not accidentally round-trip through the string table.
//s:sheetData/s:row[@r='2']/s:c[@r='A2']/@t
dimension-expanded
xl/worksheets/sheet1.xml
equal = A1:C2
dimension/@ref must expand from A1:A1 to A1:C2 to cover the new row.
//s:dimension/@ref

Render assertions

No render assertions declared.

Generator source

scripts/gen_modify_add_row.py

#!/usr/bin/env python3
"""Generate ``fixtures/xlsx/cell-bold--append-row.xlsx``.

Thin wrapper around ``scripts.mutations.xlsx_append_row``. Opens
``fixtures/xlsx/cell-bold.xlsx``, ws.append([1, 2, 3]), saves.
Satisfies ``features/xlsx/modify-add-row.json``.
"""

from __future__ import annotations

from pathlib import Path

from mutations import xlsx_append_row

_REPO_ROOT = Path(__file__).resolve().parent.parent
_IN = _REPO_ROOT / "fixtures" / "xlsx" / "cell-bold.xlsx"
_OUT = _REPO_ROOT / "fixtures" / "xlsx" / "cell-bold--append-row.xlsx"


def main() -> int:
    xlsx_append_row(_IN, _OUT)
    print(_OUT)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "xlsx/modify-add-row",
  "title": "Modify: append row to existing .xlsx",
  "format": "xlsx",
  "category": "modify-in-place",
  "summary": "Open cell-bold.xlsx (1 row: A1='Hello bold cell' bold), ws.append([1, 2, 3]), save. Assert (a) the existing row 1 is preserved intact (A1 keeps @s='1' and its shared-string reference), (b) a new row r='2' with cells A2,B2,C2 carrying numeric values 1,2,3 is present, (c) dimension ref expanded to A1:C2.",
  "roles": [
    "authoring"
  ],
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "18.3.1.73",
    "element": "row",
    "rnc_reference": "spec/ecma-376-5/part-1/rnc/SpreadsheetML.rnc",
    "xsd_reference": "spec/ecma-376-5/part-1/xsd/sml.xsd",
    "notes": "ws.append places the values into the first row below the last row that contains data. Existing rows must be byte-preserved — rewriting row 1 while appending row 2 is a common silent regression."
  },
  "fixtures": {
    "machine": "xlsx/cell-bold--append-row"
  },
  "modify": {
    "input_fixture": "fixtures/xlsx/cell-bold.xlsx",
    "mutation": "xlsx_append_row",
    "output_fixture": "fixtures/xlsx/cell-bold--append-row.xlsx"
  },
  "generator": {
    "python": "scripts/gen_modify_add_row.py"
  },
  "assertions": [
    {
      "id": "row-count-is-two",
      "part": "xl/worksheets/sheet1.xml",
      "namespaces": {
        "s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "count(//s:sheetData/s:row)",
      "must": "match",
      "value": "^2(\\.0+)?$",
      "description": "sheetData must contain exactly 2 rows."
    },
    {
      "id": "original-a1-style-preserved",
      "part": "xl/worksheets/sheet1.xml",
      "namespaces": {
        "s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "//s:sheetData/s:row[@r='1']/s:c[@r='A1']/@s",
      "must": "equal",
      "value": "1",
      "description": "Row 1's A1 must keep its bold style index. Append must not re-author pre-existing rows."
    },
    {
      "id": "original-a1-text-preserved",
      "part": "xl/sharedStrings.xml",
      "namespaces": {
        "s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "//s:si/s:t[normalize-space()='Hello bold cell']",
      "must": "exist",
      "description": "The original shared string must still be present — append of numeric values must not touch the string table."
    },
    {
      "id": "row2-a2-value",
      "part": "xl/worksheets/sheet1.xml",
      "namespaces": {
        "s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "//s:sheetData/s:row[@r='2']/s:c[@r='A2']/s:v",
      "must": "equal",
      "value": "1",
      "description": "A2 must carry the first appended value (1)."
    },
    {
      "id": "row2-b2-value",
      "part": "xl/worksheets/sheet1.xml",
      "namespaces": {
        "s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "//s:sheetData/s:row[@r='2']/s:c[@r='B2']/s:v",
      "must": "equal",
      "value": "2",
      "description": "B2 must carry the second appended value (2)."
    },
    {
      "id": "row2-c2-value",
      "part": "xl/worksheets/sheet1.xml",
      "namespaces": {
        "s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "//s:sheetData/s:row[@r='2']/s:c[@r='C2']/s:v",
      "must": "equal",
      "value": "3",
      "description": "C2 must carry the third appended value (3)."
    },
    {
      "id": "row2-cells-are-numeric",
      "part": "xl/worksheets/sheet1.xml",
      "namespaces": {
        "s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "//s:sheetData/s:row[@r='2']/s:c[@r='A2']/@t",
      "must": "not-match",
      "value": "^s$",
      "description": "A2 must NOT be shared-string-typed — numeric append must not accidentally round-trip through the string table."
    },
    {
      "id": "dimension-expanded",
      "part": "xl/worksheets/sheet1.xml",
      "namespaces": {
        "s": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "//s:dimension/@ref",
      "must": "equal",
      "value": "A1:C2",
      "description": "dimension/@ref must expand from A1:A1 to A1:C2 to cover the new row."
    }
  ]
}

Fixture

Download cell-bold--append-row.xlsx (4.8 KB)

Reference preview

No rendered reference is available for this case.

Spec notes

ws.append places the values into the first row below the last row that contains data. Existing rows must be byte-preserved — rewriting row 1 while appending row 2 is a common silent regression.